在Java中,LinkedList和ArrayList是两个常见的集合类。它们都实现了List接口,但它们在实现方式上有很大的区别。本篇博客将详细解析LinkedList和ArrayList的源码,解释它们的根本区别和表面区别,并提供详细的代码解释。
- LinkedList与ArrayList的根本区别:
- 数据结构:LinkedList是基于链表实现的,而ArrayList是基于数组实现的。
- 数据访问:LinkedList通过节点之间的链接进行数据访问,而ArrayList直接通过索引访问数组中的元素。
- LinkedList与ArrayList的表面区别: 2.1 内存占用:
- LinkedList:由于需要存储节点本身和节点之间的链接,LinkedList的内存消耗比较大。
- ArrayList:只需存储数组和元素本身,所以ArrayList相对较省内存。
2.2 插入和删除操作:
- LinkedList:由于是基于链表的数据结构,插入或删除元素时只需要调整节点之间的链接,因此插入和删除操作的时间复杂度为O(1)。
- ArrayList:由于是基于数组的数据结构,插入或删除元素时需要移动数组中的元素,时间复杂度为O(n)。
2.3 随机访问操作:
- LinkedList:由于需要从头节点开始遍历链表直到达到目标节点,所以随机访问元素的时间复杂度为O(n)。
- ArrayList:由于使用数组实现,可以通过索引直接访问数组中的元素,所以随机访问元素的时间复杂度为O(1)。
- LinkedList源码解析: LinkedList的底层实现是一个双向链表,其中的每个节点(Node)都包含了元素值、前驱节点和后继节点。它通过节点之间的链接来实现数据的添加、删除和访问操作。下面是LinkedList源码的详细解释及关键方法解析:
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
private Node<E> first; // 链表的第一个节点
private Node<E> last; // 链表的最后一个节点
private int size; // 链表的大小
private static class Node<E> {
E item; // 节点元素值
Node<E> next; // 后继节点
Node<E> prev; // 前驱节点
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
// 添加元素到链表末尾
public boolean add(E e) {
linkLast(e);
return true;
}
// 在链表指定位置插入元素
public void add(int index, E element) {
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
// 删除链表指定位置的元素
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
// 获取链表指定位置的元素
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
// 省略其他方法...
}
解释:上述代码展示了LinkedList类的部分源码。LinkedList的底层是一个双向链表,其中每个节点(Node)都包含了元素值、前驱节点和后继节点。通过add
、remove
和get
等方法,可以实现对链表的增删改查操作。
- ArrayList源码解析: ArrayList的底层实现是一个动态数组,使用数组来保存元素。在添加元素时,如果数组容量不足,ArrayList会进行扩容操作。下面是ArrayList源码的详细解释及关键方法解析:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final int DEFAULT_CAPACITY = 10; // 默认初始容量
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private transient Object[] elementData; // 真实存储元素的数组
private int size; // 数组的大小
// 构造方法
public ArrayList() {
this.elementData = EMPTY_ELEMENTDATA;
}
// 添加元素到数组末尾
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 确保数组容量足够
elementData[size++] = e; // 将元素添加到数组末尾
return true;
}
// 在数组指定位置插入元素
public void add(int index, E element) {
rangeCheckForAdd(index); // 检查插入位置是否合法
ensureCapacityInternal(size + 1); // 确保数组容量足够
System.arraycopy(elementData, index, elementData, index + 1,
size - index); // 移动元素,腾出插入位置
elementData[index] = element; // 在指定位置插入元素
size++; // 数组大小加1
}
// 删除数组指定位置的元素
public E remove(int index) {
rangeCheck(index); // 检查删除位置是否合法
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1; // 需要移动的元素个数
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved); // 移动元素,覆盖删除位置
elementData[--size] = null; // 释放最后一个元素
return oldValue;
}
// 获取数组指定位置的元素
public E get(int index) {
rangeCheck(index); // 检查索引是否合法
return elementData(index);
}
// 省略其他方法...
}
解释:上述代码展示了ArrayList类的部分源码。ArrayList的底层是一个动态数组,通过add
、remove
和get
等方法,可以实现对数组的增删改查操作。当数组容量不足时,ArrayList会进行扩容以确保数组大小的合适。
- 性能比较和适用场景:
- LinkedList适用于频繁的插入和删除操作,尤其是在列表的中间位置。它的插入和删除操作效率高。
- ArrayList适用于频繁的随机访问操作,尤其是在列表的末尾位置。它的随机访问效率高。
总结: 通过以上内容,您了解了LinkedList和ArrayList的根本区别和表面区别,并提供了详细的代码解释。LinkedList适用于频繁的插入和删除操作,而ArrayList适用于频繁的随机访问操作。根据实际需求,选择合适的集合类可以提高代码的效率和性能。
希望本篇博客对您理解和应用LinkedList和ArrayList有所帮助。如有更多问题,请参考Java官方文档和其他相关资源,以加深您的学习。祝您在应用LinkedList和ArrayList时取得成功!
标签:index,Java,LinkedList,区别,ArrayList,元素,源码,数组,节点 From: https://blog.51cto.com/u_15941034/7589358