LinkedList 1.8 源码分析
介绍
linkedList 基于链表结构实现,是一个双向链表; 链表是一种线性存储结构,要将存储的数据存在一个储存单元里,这个储存单元里除了存放有待存储的数据以外,还存储有其下一个存储单元的地址,每次查找数据的时候,通过某个存储单元中的下一个存储单元的地址寻找其后面的那个存储单元。
也就是说list中的每个元素,在存储自身值之外,还额外存储了其前一个和后一个元素的地址,所以 也就可以很方便地根据当前元素获取到其前后的元素
主要内部类
Node
该类一个元素,二个Node节点,一前一后,描述了一个带有二个箭头的数据节点,也就是双向链表
该类为静态内部类,若不使用static修饰,那么Node就是一个普通的内部类,在java中,一个普通内部类在实例化之后,默认会持有外部类的引用,这就有可能造成内存泄露。但使用static修饰过的内部类,就不会有这种问题
成员变量
transient int size = 0; //节点个数
transient Node<E> first; //头结点
transient Node<E> last; //尾节点
构造函数
LinkedList()
内部未做任何操作
LinkedList(Collection<? extends E> c)
先调用空参构造,然后调用addAll()方法把即可插入
方法
addAll(int index, Collection<? extends E> c)
将指定集合中的所有元素插入到此列表中,从指定的位置开始。
首先确认是否会索引越界,将参数集合转数组,判断数组长度是否0,为0 直接返回false;声明二个node,用于
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//判断index是否越界,越界则抛出异常
Object[] a = c.toArray();
int numNew = a.length;//要插入的集合的长度
if (numNew == 0)
return false;
//声明pred和succ两个Node对象,用于标识要插入元素的前一个节点和最后一个节点
Node<E> pred, succ;
if (index == size) { //如果size等于原数组长度则表示在结尾添加
succ = null; //最后节点为null
pred = last; //前一个节点为最后一个节点
} else {
succ = node(index);//index位置上的Node对象
pred = succ.prev; //设置为index位置的前一个节点
}
for (Object o : a) { //遍历要插入的集合
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
//如果要插入的位置的前一个节点为null表示是第一个节点,则直接将newNode赋给第一个节点
first = newNode;
else
//将要插入的集合元素节点对象赋给此位置原节点对象的前一个对象的后一个,
//即更改前一个节点对象的next指针指到新插入的节点上
pred.next = newNode;
//更改指向后将新节点对象赋给pred作为下次循环中新插入节点的前一个对象节点,依次循环
pred = newNode;
}
//此时pred代表集合元素的插入完后的最后一个节点对象
if (succ == null) { //结尾添加的话在添加完集合元素后将最后一个集合的节点对象pred作为last
last = pred;
} else {
pred.next = succ;//将集合元素的最后一个节点对象的next指针指向原index位置上的Node对象
succ.prev = pred;//将原index位置上的pred指针对象指向集合的最后一个对象
}
size += numNew;
modCount++;
return true;
}
Node<E> node(int index) {
//判断index是否小于size的一半,如果小于则从头遍历节点,否则从结尾遍历节点
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next; //从first第一个节点开始,依次将后一个节点赋给x
return x; //返回index位置上的Node对象,下同理
} else { //与 if 中相反
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
get(int index)
返回此列表中指定位置的元素。
其实就是判断是否索引越界后调用了上述方法中的node(int index)方法,然后返回该节点的item
set(int index, E element)
用指定的元素替换此列表中指定位置的元素。
判断是否索引越界后调用了上述方法中的node(int index)方法获取该索引的节点,然后将节点的item设置为新元素,最后返回旧节点的元素
add(E e)
将指定的元素追加到此列表的末尾。
调用的 linkLast(E e) 方法
void linkLast(E e) {
final Node<E> l = last; //定义最后一个node节点
final Node<E> newNode = new Node<>(l, e, null); //将最后节点new一个最新的节点
last = newNode; //将最后节点改为新new的
if (l == null) //表示为第一个添加的元素
first = newNode;
else
l.next = newNode; 将之前的最后一个节点的下一个节点引用最新的节点
size++; //长度自增
modCount++;
}
add(int index, E element)
在此列表中的指定位置插入指定的元素。
如果索引与size大小相等就说明是在尾部添加,调用 linkLast(E e) 方法,否则调用linkBefore(E e, Node succ) 方法,传的参数为当前索引节点,该方法与linkLast(E e) 类似,只不过定义的节点不是最后一个node节点,而是参数的前一个节点succ.prev
addLast(E e)
将指定的元素追加到此列表的末尾。
调用 linkLast(E e) 方法
addFirst(E e)
在该列表开头插入指定的元素。
调用 linkFirst(e) 方法,逻辑与 linkLast(E e) 方法相反
clear()
从列表中删除所有元素。
从第一个节点开始循环遍历,当节点不为null停止,将节点的内的属性都设为null
indexOf(Object o)
返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
其实就是for循环,然后判断该节点的item是否与参数一致,最后返回索引
remove()
删除此列表的第一个元素。
主要调用unlinkFirst(Node f)方法
private E unlinkFirst(Node<E> f) {
final E element = f.item; //定义一个变量element指向待删除节点的值
final Node<E> next = f.next; //定义一个变量next指向待删除节点的下一个节点
f.item = null; //将该节点的数据删除
f.next = null;
first = next; //下一个节点设置为头节点,删除上一个节点
if (next == null) //判断下一个节点是否为空
last = null; //把last设置为空
else
next.prev = null; //把next的prev设置为空,因为next现在指代头节点。
size--;
modCount++;
return element;
}
remove(int index)
删除该列表中指定位置的元素。
E unlink(Node<E> x) {
final E element = x.item; //定义一个变量element指向待删除节点的值
final Node<E> next = x.next; //定义一个变量next指向待删除节点的下一个节点
final Node<E> prev = x.prev; //定义一个变量prev指向待删除节点的上一个节点
if (prev == null) { //如果上一个节点为null表示为第一个节点
first = next; //设置新的开始节点
} else {
prev.next = next; //将传入的节点对象跳过
x.prev = null; //删除节点
}
if (next == null) { //如果下一个节点为null表示为最后节点
last = prev; //设置新的最后节点
} else {
next.prev = prev; //把待删除的节点的前、后节点链接起来
x.next = null; //删除节点
}
x.item = null; //删除节点
size--;
modCount++;
return element;
}
remove(Object o)
从列表中删除第一次出现的指定元素
for循环获取该元素,然后调用 unlink(Node x) 方法