LinkedList中addAll(Collection<? extends E> C)方法解析

本文长度还算比较长,如果你觉得过于冗长,可以你可以只看源码图片部分。但是请注意图中的绿色箭头,它代表了操作步骤。

public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0)
            return false;

        Node<E> pred, succ;
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            succ = node(index);
            pred = succ.prev;
        }

        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }

        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
    }

1

说明:以上节点命名均为LinkedList底层名称。其中,
prev代表节点指向前面节点的指针,next代表节点指向后面节点的指针。
first代表指向头节点的指针;last代表指向尾节点的指针。
上方的两个节点代表待插入节点,它们是中的数据部分e来自集合c,由Object[] a = c.toArray()进行转换,并在Node<E> newNode = new Node<>(pred, e, null);中创建待插入节点。

@SuppressWarnings(“unchecked”)是一个注解但是与原理解析无关紧要,除非你想看注解。

checkPositionIndex(index); // 验证你给出的插入位置是否合法

Object[] a = c.toArray(); // 将你的集合c转为数组a
int numNew = a.length;
if (numNew == 0) // 集合c为空返回失败
return false;
 Node<E> pred, succ; // 用于添加操作的两个临时的指针,它们很关键,请记住它们的名字
 if (index == size) { //确定你的插入位置是否在末尾
    succ = null;
    pred = last;
 } else {
 succ = node(index); //当你的插入位置不在末尾时,node方法用于确定你指定位置的节点。
 pred = succ.prev; // 将指定节点的前向指针赋值给pred,如图
 }

2
此时,succ指向index位置处的节点。addAll(Collection<? extends E> C)是插入在指定位置的,所以最后指定位置的节点相当于向后移动。
接下来,记住指针就是地址。所以pred中存放的是前一个节点的地址。

 for (Object o : a) {
     @SuppressWarnings("unchecked") E e = (E) o;
     Node<E> newNode = new Node<>(pred, e, null);
     if (pred == null)
        first = newNode;
     else
        pred.next = newNode;
     pred = newNode;
 }

由于节点的创建,上图将发生变化
3
pred.next = newNode;
4
pred = newNode;
5
接下来循环重复
6
7
8
此时循环结束,来到下面

if (succ == null) {
  last = pred;
  } else { // 插入位置不在末尾时进入else
  pred.next = succ;
  succ.prev = pred;
 }

pred.next = succ;
9
succ.prev = pred;
10
整理一下
11
进一步整理,得到结果。
12

size += numNew; // 插入元素后,size计数值将增大
modCount++; // 无关语句,不需要在意
return true;

指针即地址
​ 当你看到指针时,不应当仅将其想象为箭头,而是当你在分析当前节点的指针操作时应当想象指针所存储的地址;当你在移动指针时将其想象为箭头。遵循这种顺序你便可以清晰的区别源码中各个指针出现的意义以及它们所指向的节点。

END

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值