Collection容器初探之LinkedList

网上的LinkedList的源码分析大都为JDK1.6的版本,所以为双向循环链表的环形结构。但是JDK1.7的版本为双向链表。

先简单分析了解下JDK1.6版本的源码:

参考:

给jdk写注释系列之jdk1.6容器(2)-LinkedList源码解析
这篇博客分析的很彻底!!!赞。


JDK1.6~JDK1.7过程中,LinkedList从双向循环链表转换为双向链表

这次来简单了解和分析下JDK1.6的基于双向循环链表的实现:

这里写图片描述

继承自List,Deque,Cloneable,Serializible接口



public class LinkedList
    extends AbstractSequentialList
    implements List, Deque, Cloneable, java.io.Serializable


双向循环链表的底层数据存储形式:


 private transient Entry header = new Entry(null, null, null);
    private transient int size = 0;

其中size为元素数目,header为链表的头节点,Entry为链表中的节点对象

经常使用的默认构造方法:


  public LinkedList() {
   //将header节点的前一节点和后一节点都设置为自身
        header.next = header.previous = header;
    }


Entry为LinkedList的静态内部类,定义了当前存储的节点和它相邻的两个节点:



    private static class Entry {
    E element;
    Entry next;
    Entry previous;

    Entry(E element, Entry next, Entry previous) {
        this.element = element;
        this.next = next;
        this.previous = previous;
    }
    }


插入:

这里写图片描述

    /**
     * 添加一个集合元素到list中
     */
    public boolean addAll(Collection<? extends E> c) {
            // 将集合元素添加到list最后的尾部
        return addAll(size , c);
    }

    /**
     * 在指定位置添加一个集合元素到list中
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        // 越界检查
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException( "Index: "+index+
                                                ", Size: "+size );
        Object[] a = c.toArray();
        // 要插入元素的个数
        int numNew = a.length ;
        if (numNew==0)
            return false;
        modCount++;

        // 找出要插入元素的前后节点
        // 获取要插入index位置的下一个节点,如果index正好是lsit尾部的位置那么下一个节点就是header,否则需要查找index位置的节点
        Entry<E> successor = (index== size ? header : entry(index));
        // 获取要插入index位置的上一个节点,因为是插入,所以上一个点击就是未插入前下一个节点的上一个
        Entry<E> predecessor = successor. previous;
        // 循环插入
        for (int i=0; i<numNew; i++) {
            // 构造一个节点,确认自身的前后引用
            Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);
            // 将插入位置上一个节点的下一个元素引用指向当前元素(这里不修改下一个节点的上一个元素引用,是因为下一个节点随着循环一直在变)
            predecessor. next = e;
            // 最后修改插入位置的上一个节点为自身,这里主要是为了下次遍历后续元素插入在当前节点的后面,确保这些元素本身的顺序
            predecessor = e;
        }
        // 遍历完所有元素,最后修改下一个节点的上一个元素引用为遍历的最后一个元素
        successor. previous = predecessor;

        // 修改计数器
        size += numNew;
        return true;
    }

删除:



/**
     * 删除第一个匹配的指定元素
     */
    public boolean remove(Object o) {
         // 遍历链表找到要被删除的节点
        if (o==null) {
            for (Entry e = header .next; e != header; e = e.next ) {
                if (e.element ==null) {
                    remove(e);
                    return true;
                }
            }
        } else {
            for (Entry e = header .next; e != header; e = e.next ) {
                if (o.equals(e.element )) {
                    remove(e);
                    return true;
                }
            }
        }
        return false;
    }

    private E remove(Entry e) {
        if (e == header )
           throw new NoSuchElementException();

       // 被删除的元素,供返回
        E result = e. element;
       // 下面修正前后对该节点的引用
       // 将该节点的上一个节点的next指向该节点的下一个节点
       e. previous.next = e.next;
       // 将该节点的下一个节点的previous指向该节点的上一个节点
       e. next.previous = e.previous;
       // 修正该节点自身的前后引用
        e. next = e.previous = null;
       // 将自身置空,让gc可以尽快回收
        e. element = null;
       // 计数器减一
        size--;
        modCount++;
        return result;
    }


查询:

/**
     * 查找指定索引位置的元素
     */
    public E get( int index) {
        return entry(index).element ;
    }

    /**
     * 返回指定索引位置的节点
     */
    private Entry<E> entry( int index) {
        // 越界检查
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException( "Index: "+index+
                                                ", Size: "+size );
        // 取出头结点
        Entry<E> e = header;
        // size>>1右移一位代表除以2,这里使用简单的二分方法,判断index与list的中间位置的距离
        if (index < (size >> 1)) {
            // 如果index距离list中间位置较近,则从头部向后遍历(next)
            for (int i = 0; i <= index; i++)
                e = e. next;
        } else {
            // 如果index距离list中间位置较远,则从头部向前遍历(previous)
            for (int i = size; i > index; i--)
                e = e. previous;
        }
        return e;
    }

改动:



/**
     * 修改指定位置索引位置的元素
     */
    public E set( int index, E element) {
        // 查找index位置的节点
        Entry e = entry(index);
        // 取出该节点的元素,供返回使用
        E oldVal = e. element;
        // 用新元素替换旧元素
        e. element = element;
        // 返回旧元素
        return oldVal;
    }



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值