集合的实现原理【二】

  • 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;
        }
    }

链表的这种托盘的结构决定了它的的特点:

  1. 数据的写入很快,但是数据的读取很慢。
  2. 数据没有扩容的概念。

在链表中有两个托盘:firstNode和lastNode。

    transient Node<E> first;
    transient Node<E> last;

在第一次添加元素时,新的元素既是firstNode也是lastNode,这点可以在源码中体现。

  • 链表中添加(add)方法解析
    transient int size = 0;

    public void add(int index, E element) {
        checkPositionIndex(index);
        //checkPositionIndex方法是用来检测index是否在[0,size)中,在返回index,否则抛出
        //IndexOutOfBoundsException异常。
        if (index == size)
            linkLast(element); //添加第一个元素
        else
            linkBefore(element, node(index)); //添加第N个元素。
    }
    
    

在add方法中判断是不是第一次添加,size在这里的默认值为0,当第一次添加是index为0,执行linkLast方法。

    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++;
    }
    /* 这里的last变量是上文中的托盘 Node<E> last;
     * l, e, null 变量的意思是上家,数据元素,下家,这里下家为null.
     * 当l == null 时,设置first = newNode;这句话的意思就是
     * 【在第一次添加是】新元素既是第一个也是最后一个。
     */

在这里体现了第一个添加的元素既是FirstNode也是LastNode这句话。

而下面这句话 l.next = newNode 体现了链表每个托盘是双向关联的。

当我们添加的数据不是第一个是,就会执行linkBefore方法,但是在分析linkBefore方法之前,我们先来分析一下 Node<E> node(int index)

这个方法:

    Node<E> node(int index) {
        if (index < (size >> 1)) { //判断index在前半段还是在后半段。
            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;
        }
    }

这个方法是链表中的查询方法,index在前半段时取出第一个托盘 Node<E> x = first ,往后查 x = x.next

当index在后半段时取出最后一个托盘 Node<E> x = last ,往前查 x = x.prev 。

当找到要插入的位置时,返回所在的托盘, linkBefore(E e, 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;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }
    /* 首先获取succ的上家记做pred,将pred当做上家传入新创建的托盘newNode中,将succ当做下家传入
     * newNode 中,最后形成 new Node<>(pred, e, succ)。
     * 如果pred不存在,那就把NowNode设置为firstNode。否则,将
     * pred的下家设置为newNode。
     */

以上总结是个人观点,如有存在错误请指正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值