异:
-
数据结构上:ArrayList底层数据结构是数组,而LinkedList采用的是双向链表。
-
查找,删除,插入元素上:因为ArrayList是数组,所以查找元素的时间复杂度为O(1),删除和插入元素,需要大量的移动元素,时间复杂度为O(n)。
LinkedList虽然是双向链表,但他的删除元素remove(Element e)和删除指定位置元素remove(int index)的时间复杂度并不是O(1),只是近似O(1), 因为只有你直接传进来的是链表节点,时间复杂度才是O(1),而这里删除元素,传进来的都不是节点。需要LinkedList去自己通过node(int index)方法找到位置对应的节点的前一个节点才能改变节点的next指向进行删除。如果直接删除元素,没有传进来元素的index,就LinkedList就需要从头开始遍历找到元素。插入操作也是,如果是中间插入,也需要遍历链表,得到index对应的元素
JDK1.7及以后的版本做了优化,里面有fist和last两个引用分别指向双向链表的首节点和尾结点,通过比较int index离哪一端比较近,就从哪一端开始遍历,找到对应位置的节点
查找元素的时间复杂度就更是O(n)了。所以并不推荐使用LinkedList。
- ArrayList支持随机访问,它实现了RandomAccess接口,但RandomAccess接口只是一个声明式接口,告诉Collection的binarySearch()方法,ArrayList支持随机访问,调用
indexedBinarySearch()方法。因为indexedBinarySearch()方法中就是通过midVal = list.get(mid)来得到中间值。
而 LinkedList没有索引,不支持随机快速访问,通过iteratorBinarySearch()方法,迭代器遍历。
4. 实现了RandomAccess接口的ArrayList ,优先选择普通for循环 ,其次foreach。因为其内部数组的索引i是可以快速访问到元素。
未实现RandomAccess接口的LinkedList, 优先选择iterator遍历。如果在for循环中,相当于是两层嵌套,每一个i, 都要从头或者从尾开始遍历,时间复杂度为O(n2)。而foreach遍历底层是通过iterator实现的,其内部curr的next可以依次指向下一个节点。
同:
都是线程不安全的。
参考:https://www.cnblogs.com/NickyYe/p/4461454.html
https://blog.csdn.net/m0_37884977/article/details/80467658
芋道源码
Linkedlist近似O(1)问题:LinkedList 的 源码
node(int index)
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
LinkedList中删除指定元素, 没有index, 从头开始遍历
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
移除指定位置上的元素,用到node(index)。
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
在指定的位置上添加新的元素,如果不是在链表尾部添加的话,也有通过node(index)来遍历index对应的节点。不过这种情况比较少,一般都是在依次添加。
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
增加一个新的元素:(新添加的元素位于LinkedList的头结点),时间复杂度:O(1),因为LinkedList有一个fist索引指向头结点.
public void push(E e) {
addFirst(e);
}
indexedBinarySearch()和iteratorBinarySearch()源码:
Collections是集合的一个工具类,我们看一下Collections源码中的二分搜索方法。
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);
}
判断list是否是RandomAccess的实例,如果是,则执行indexedBinarySearch方法,如果不是,则执行iteratorBinarySearch方法
private static <T>
int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
int low = 0;
int high = list.size()-1;
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = list.get(mid);
int cmp = midVal.compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
private static <T>
int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
{
int low = 0;
int high = list.size()-1;
ListIterator<? extends Comparable<? super T>> i = list.listIterator();
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = get(i, mid);
int cmp = midVal.compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
实现了RandomAccess接口的List使用索引遍历,而未实现RandomAccess接口的List使用迭代器遍历。