一步一步解析java集合框架LinkedList源码(3)

时间:2022-12-16 17:21:18

我在阅读源码的过程中很多时候是没有头绪的。所以为了避免大家也遇到这种状况,源码不求全求大,做到“透过实践看源码”,分块分层。

介绍一些额外的添加操作

  • 指定索引进行插入
    public static void main(String[] args) {
LinkedList<String> list=new LinkedList<String>();
list.add("first");
list.add("second");
list.add(0,"zero");
System.out.println(list.getFirst());
System.out.println(list.getLast());
}

运行结果:

zero
second

    public static void main(String[] args) {
LinkedList<String> list=new LinkedList<String>();
list.add("first");
list.add("second");
LinkedList<String> list2=new LinkedList<String>();
list.add("first2");
list.add("second2");
list.addAll(list2);
System.out.println(list.getFirst());
System.out.println(list.getLast());
}

运行结果:

first
second2

源码分析:

    /**
* 将集合c中的元素全部添加进链表
*/

public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}

/**
*将集合c中的元素全部添加进链表
*addAll(Collection<? extends E> c)函数调用传递参数index==size
*带参构造函数index==size==0
*/

public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);
//将集合c转化成数组
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;

Node<E> pred, succ;
//index==size;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
//遍历数组,每次添加至尾节点
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
//构造新节点,prev(前一节点)节点均是尾节点,第一次直接是last节点,以后每次是更新的尾节点
Node<E> newNode = new Node<>(pred, e, null);
//pred==null,表明原链表没有数据
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;//将新构建的节点作为下个新节点的prev节点
}
//succ==null
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}

size += numNew;
modCount++;
return true;
}
    /**
* 通过索引添加元素
*/

public void add(int index, E element) {
checkPositionIndex(index);

if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
//检查索引的合理性
private void checkPositionIndex(int index) {
//索引超出范围,抛出异常
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 索引值必须在合法范围内
*/

private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
/**
* 返回对应索引index节点
*/

Node<E> node(int index) {
// assert isElementIndex(index);
//size >> 1 表示size/2,通过这种比较可以更快的查找到节点
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;
}
}
/**
* 插入节点
* 在节点Node<E> succ前面插入
*/

void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
//首先构造新节点
final Node<E> newNode = new Node<>(pred, e, succ);
//重新进行双向链接
succ.prev = newNode;
//在首节点处插入,pred==null
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
/**
* 将元素e添加至链表末尾,
* 双向的,类似于:first--->A---->B---->...--->last
* <-- <--- <--- ...<--
*/

void linkLast(E e) {
//第一次添加数据,因为last并没有实例化,所以l不是指向last的引用,l=null
final Node<E> l = last;
//根据添加的元素创建一个节点,将last节点作为prev节点,next节点设置为空,随后设置
final Node<E> newNode = new Node<>(l, e, null);
//将新构建的节点作为last节点,保证每次添加都添加至末尾
last = newNode;
//第一次添加,将first指向newNode节点(新构建的)
if (l == null)
first = newNode;
else
l.next = newNode;//以后每次都将最后一个节点的next只想新节点
size++;//元素数递增
modCount++;
}