Java基础

Arraylist与LinkedList区别

Posted by Chen Xingxu on April 26, 2020

Arraylist 与 LinkedList 区别?

  1. 是否保证线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

  2. 底层数据结构:ArrayList 底层使用的是 Object 数组;LinkedList 底层使用的是双向链表数据结构(JDK 1.6 之前为循环链表,JDK 1.7 取消了循环。注意双向链表和双向循环链表的区别)。

  3. 插入和删除是否受元素位置的影响:

    ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。比如:执行 add( E e )方法的时候,ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话,即 add( int index, E element ),时间复杂度就为 O( n - i )。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的 ( n - i ) 个元素都要执行 向后/向前 位移一位的操作。

    LinkedList 采用链表存储,所以对于 add( E e )方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置 i 插入和删除元素的话,即 add(int index, E element),时间复杂度近似为 O(n),因为需要先移动到指定位置再插入。

  4. 是否支持快速随机访问:LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象,对应于 get(int index) 方法。

  5. 内存占用空间:ArrayList 的空间浪费主要体现在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在他的每一格元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。

RandomAccess接⼝

public interface RandomAccess {
}

查看源码我们发现实际上 RandomAccess 接口中什么都没有定义。所以,在我看来 RandomAccess 接口不过是一个标识罢了。标识什么?标识实现这个接口的类具有随机访问功能。

在 binarySearch() 方法中,他要判断传入的 list 是否是 RandomAccess 的实例,如果是,调用 indexedBinarySearch() 方法;如果不是,那么调用 iteratorBinarySearch() 方法。

public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T
key) {
	if (list instanceof RandomAccess || list.size()
	<BINARYSEARCH_THRESHOLD)
		return Collections.indexedBinarySearch(list, key);
	else
		return Collections.iteratorBinarySearch(list, key);
}

ArrayList 实现了 RandomAccess 接口,而 LinkedList 没有实现。为什么呢?我觉得还是和底层数据结构有关。ArrayList 底层是数组,而 LinkedList 底层是链表。数组天然支持随机访问,时间复杂度为 O(1),所以称为快速随机访问。链表需要遍历到特定位置才能访问特定位置的元素,时间复杂度为 O(n),所以不支持快速随机访问。ArrayList 实现了 RandomAccess 接口,就表明了他具有快速随机访问功能。RandomAccess 接口只是标识,并不是说 ArrayList 实现了RandomAccess 接口才具有快速随机访问功能的。

下面再总结一下 list 的遍历方式选择:

  • 实现了 RandomAccess 接口的 list,优先选择普通 for 循环,其次 foreach;
  • 未实现 RandomAccess 接口的 list,优先选择 iterator 遍历(foreach 遍历底层也是通过 iterator 实现的),大 size 的数据,千万不要使用普通 for 循环。

双向链表和双向循环链表

双向链表:包含两个指针,一个 prev 指向前一个结点,一个 next 指向后一个结点。

双向循环链表:最后一个结点的 next 指向 head ,而 head 的 prev 指向最后一个结点,构成一个环。