上一篇文章简单的整理了ArrayList的源码。想一想干脆把java几个常用的集合全部分析一遍。接下来讲解一下LinkedList的源码。
LinkedList与双向链表
linkedList本质是一个双向链表,所以在分析其源码时,首先要搞清楚什么是双向链表,双线链表有哪些特点:
- 灵活的空间要求,存储空间不需要连续。
- 不支持下标访问,支持顺序遍历检索。
- 增对增删效率会更高些,只和操作节点的前后节点有关系,无需移动元素。
源码分析
LinkedList是通过双向链表去实现的,他的数据结构具有双向链表的优缺点,既然是双向链表,那么的它的顺序访问效率会非常高,而随机访问的效率会比较低,它包含一个非常重要的私有内部静态类:Node
Node静态类
LinkedList内部实现了一个静态类,node
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;
}
}
get方法
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
//判断下标是否合法
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
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;
}
}
从源代码上可以看出,其实get方法的本质上还是遍历链表。而在遍历链表时,它会根据给出的index和集合长度进行比较,如果大于集合长度的一半就从尾部节点遍历,否则从头部节点遍历。
set方法
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
set方法还是比较简单的,基本上就做了以下几件事情:
- 校验下标的准确性
- 更新元素内容
- 返回旧元素内容
add方法
public boolean add(E e) {
//从尾部添加元素
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
set方法是直接从双向链表的尾部添加元素。