这篇文章我们先从一个面试问题出发,然后逐步详细分析这三个集合类的原理。
Q: 请你谈一谈,Vector, ArrayList, LinkedList 有什么区别?
下面的答案源引自极客时间,杨晓峰《Java核心技术面试精讲》。
A:
这三者都是实现集合框架中的 List,也就是所谓的有序集合,因此具体功能也比较近似,比如都提供按照位置进行定位、添加或者删除的操作,都提供迭代器以遍历其内容等。但因为具体的设计区别,在行为、性能、线程安全等方面,表现又有很大不同。
Vector是 Java 早期提供的线程安全的动态数组,如果不需要线程安全,并不建议选择,毕竟同步是有额外开销的。Vector 内部是使用对象数组来保存数据,可以根据需要自动的增加容量,当数组已满时,会创建新的数组,并拷贝原有数组数据。
ArrayList 是应用更加广泛的动态数组实现,它本身不是线程安全的,所以性能要好很多。与 Vector 近似,ArrayList 也是可以根据需要调整容量,不过两者的调整逻辑有所区别,Vector 在扩容时会提高 1 倍,而 ArrayList 则是增加 50%。
LinkedList 顾名思义是 Java 提供的双向链表,所以它不需要像上面两种那样调整容量,它也不是线程安全的。
从这个问答入手,这篇文章会延伸开三个方向。首先自然是牵扯到的数据结构与算法,其次就是 Java 集合框架的设计结构,最后是源码分析。
Vector
和 ArrayList
都是用数组实现的,LinkedList
是用链表实现的,关于基础的数据结构我不再赘述,想对这两个数据结构有进一步了解的同学可以跳转去看我下面的文章。
【数据结构与算法】->数据结构->数组
【数据结构与算法】->数据结构->链表->LRU缓存淘汰算法的实现
关于数组和链表。一定要明确它们的几个基础操作的时间复杂度,比如Access, Insert, Delet。
到这里自然是少不了基础的算法,基本的排序一定要清楚,特别是 O(nlogn) 的三个(堆排序,归并排序,快排),这三个排序不仅要很熟练地写出来,而且很多力扣题都可以用到这三种排序的思想,几个线性排序算其实用的也不多,不过在我刷力扣的时候也碰到了几个要用计数排序和桶排序思想的题,所以九种排序方法尽量还是要能熟练地写出来,最重点的就是 nlogn 的三个。
【数据结构与算法】->算法->排序(一)->冒泡排序&插入排序&选择排序
【数据结构与算法】->算法->排序(二)->归并排序&快速排序
【数据结构与算法】->算法->排序(三)->线性排序->桶排序&计数排序&基数排序
【数据结构与算法】->数据结构->堆(上)->详解堆&堆排序
排序除了要考虑到时间复杂度以外,还要清楚哪些排序是稳定的,哪些是不稳定的(快排,堆排,希尔,选择)。有篇文章整理的十大经典排序也非常好,大家可以看看。
十大经典排序算法(动图演示)
关于排序这里再补充一点,就是我很多小伙伴去面试都被问到了大规模数据外部排序的问题。主要就是归并,快排这样的可以分割文件的算法,关于这部分内容我会再整理一篇文章出来。
其实数组的排序相对容易,链表的操作有时候会非常麻烦,力扣上有道关于链表的题非常好,我也是放在这里。
#146 LRU Cache
LRU Cache也算是链表的一个经典应用,要实现起来有两个思路,一个是手动实现一个双向链表 + HashMap,一个就是直接 extends
一个 LinkedHashMap
然后重写一个删除的方法就好了,这里我还是建议自己手动实现一个双向链表,可以向面试官体现自己的基本功。
还有一道题是 #155 最小栈,这道题基本思路就是建立一个普通数据栈,一个最小值的栈,然后两个栈一起配合完成最小栈的功能。但是这道题还有一个思路非常巧妙,就是实现了一个链表,通过维护这个链表就可以实现了,并不需要两个容器。所以说链表在实际应用中是非常灵活的,大家一定不要被限制了想象力。如果这道题用链表的方式实现的话,我相信面试官也会眼前一亮的。
代码我贴在下方。
private Node head;
public MinStack() {
}
public void push(int x) {
if (this.head == null) {
this.head = new Node(x, x);
}