上一篇中解读了一下开发中最常用到的ArrayList在jdk1.7 和1.8中的不同,以及了解扩容机制,严格上来说,两个版本没有很大的改动,本文将接着对Linked List进行解读。
先看Linked List继承关系图谱
通过知识图谱,可以看到LinkedList是继承了AbstractSequentiaList,实现了List 、Deque Cloneable 、Serializable 等接口。
继承AbstractSequentiaList:
实现List:说明ArrayList是存取有序,可以有重复元素,且元素可以是为null值;
实现Deque:说明ArrayList同时也具备队列的特性:
实现Cloneable:说明支持拷贝,且是浅拷贝;
实现Serializable:说明可以实现序列化。
全局变量
构造函数
对构造函数进一步解读
//无参构造方法
public LinkedList() {
}
//通过一个集合初始化LinkedList,元素顺序有这个集合的迭代器返回顺序决定
public LinkedList(Collection<? extends E> c) {
// 调用无参构造函数
this();
// 添加集合中所有的元素
addAll(c);
} 由于LinkedList底层是链表结构,在扩容时候非常方便,这于数据结构密切相关,接着再看;
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;
}
void linkLast(E e) {
// 得到最后一个节点
final Node<E> l = last;
// 将前驱节点位置,元素,后继节点为null作为参数新生一个节点
final Node<E> newNode = new Node<>(l, e, null);
// 最后一个节点
last = newNode;
// 如果最后节点是null ,那么第一个节点是新节点;如果不是null,下一个节点是新节点
if (l == null)
first = newNode;
else
l.next = newNode;
// 长度加1
size++;
modCount++;
}指定索引位置添加元素
public void add(int index, E element) {
// 判断指定位置是否合法
checkPositionIndex(index);
// 如果指定位置在尾部,则通过尾插法来插入指定元素
if (index == size)
linkLast(element);
else
//如果指定位置不是尾部,则添加到指定位置前
linkBefore(element, node(index));
}
// 判断指定位置是否合法
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
//检查索引位置
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//判断插入元素时指定位置是否合法
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
ArrayList list =new LinkedList();
list.add(1);//当第一次add时候,将Node节点的信息设置为,前驱为null,存储元素数据,后驱节点保留添加元素的思路:
首先创建一个Node节点,同时将节点的前驱和后继分别设置为前一个节点的后继和null,如果是指定位置插入,则将前驱节点保存为指定位置前节点,后继节点保存为指定位置后节点的前驱。