Arraylist 与 LinkedList 区别?
-
是否保证线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
-
底层数据结构:ArrayList 底层使用的是 Object 数组;LinkedList 底层使用的是双向链表数据结构(JDK 1.6 之前为循环链表,JDK 1.7 取消了循环。注意双向链表和双向循环链表的区别)。
-
插入和删除是否受元素位置的影响:
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),因为需要先移动到指定位置再插入。
-
是否支持快速随机访问:LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象,对应于 get(int index) 方法。
-
内存占用空间: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 指向最后一个结点,构成一个环。