开始
ArrayList
与LinkedList
是两种不同的List
接口的实现类,具体各自的实现接口和继承类见下图:
通过继承及实现关系我们可以得知其各自特性的来源。
ArrayList
ArrayList
在实例化时可以指定初始容量,如果不指定初始容量,会使用默认的初始容量,其默认的初始容量为10.当其中的元素达到容量上限时,如果再有新元素加入时,ArrayList
会自动扩容,你也可以通过ensureCapacity(int minCapacity)
方法指定当原ArrayList
的容量还剩下minCapacity
时既进行扩容。但是在使用该方法时可能会减少再分配空间的数量。ArrayList
的可变大小的数组的特性实现自List
接口,实现了所有的可选择的list操作,并且允许所有类型的元素被添加(包括null)。除了实现List
接口外,ArrayList
还提供了一些方法来操作内部用来存储列表的数组的大小。
需要注意的是,ArrayList
并不是线程安全(同步)的。如果同时有多个线程获得了同一个ArrayList
实例并至少有一个线程要对ArrayList
中的值进行写操作,则必须在外部对这个实例对象进行同步操作,如使用synchronized
等。如果在创建该实例的时候就希望获得线程安全的实例对象时,那么你可以使用List list = Collections.synchronizedList(new ArrayList(...))
。
得益于数组的特性,ArrayList
在进行get(int index)
操作时只有O(1)的时间复杂度,在容量范围内进行add操作时也是O(1)的时间复杂度。但是在进行容量外添加,或者在进行对数组内元素进行移除,对数组进行迭代移除或迭代增加时的时间复杂度就上升为了O(n)。ArrayList
允许快速随机的读取访问,所以在使用时可以在常量时间内获取到里面的任何元素。但是,在添加或者移除非最后一位元素时,由于需要将被添加或修改元素后的元素进行移动位置,或者在添加超过最大容量数目的元素时,由于需要新建一个新的数组(容量一般为原数组容量的1.5倍)并对原数组的元素进行复制至新数组,所以在进行上述操作时所需要的时间上升到了常量时间(O(n))。
LinkedList
不同于ArrayList
的动态数组的实现方式,LinkedList
的实现方式是双向链表。它的双向链表功能实现自List
和Deque
接口,和ArrayList
一样,LinkedList
允许所有的元素类型,包括了null。由于实现方式为双端链表,那么在访问给定索引处的元素时,只能从数组的头部或者尾部(取决于哪个离索引处更近)开始挨个遍历直到抵达索引处。
与ArrayList
相同的需要注意的是,LinkedList
也不是线程安全(同步)的,与ArrayList
一样,想要线程安全可以通过线程同步控制来解决,也就是使用synchronized
关键字。当然,在创建该实例时就希望获得线程安全的特性的时候,可以使用List list = Collections.synchronizedList(new LinkedList(...))
。
得益于双向链表的特性,LinkedList
在进行add(E element)
、Iterator.remove()
、ListIterator.add(E element)
操作时的时间复杂度为常量时间(O(1)),并且在头部进行元素添加操作时(add(int 0, E element)
)的时间复杂度也为常量时间。在进行get(int index)
、add(int index, E element)
、remove(int index)
操作时由于需要从头(或者尾)挨个遍历到待操作元素,所以进行如上操作时所需要的时间复杂度为线性时间(o(n)(平均步骤为n/4))。LinkedList
允许你在使用迭代器时在常量时间内对元素进行添加或者移除操作,但是却只能顺序访问元素。也就是说,你可以从头遍历或者从尾遍历,但是找到一个元素的位置消耗的时间和列表的大小是成正比的。
使用时如何选择
使用LinkedList
的最大优势体现在你需要大量复用迭代器来进行插入和删除元素的操作。而使用ArrayList
的最大优势体现在你需要频繁的查找某一位置的元素,并且很少进行元素的添加及移除操作时。
如果你有大量的元素需要存储的时候,由于LinkedList
内对每个元素需要额外记录它的前序元素和后序元素(双端链表的关系),在内存空间的占用上要高于ArrayList
。但是由于ArrayList
无论在容量内元素是否占满,都需要为容量分配内存,所以在使用ArrayList
时不建议在元素较少时分配过大的初始容量。同时,由于ArrayList
的初始容量默认值很小(初始值为10),在使用时当需要添加大量的元素时,为了避免自动调整ArrayList
的大小的高成本,建议新建一个大容量的ArrayList
然后进行元素添加。