linkedlist源码浅解析

Linkedlist 底层是双向链表实现的的,他是一个链表类的数据结构 即线性结构-链表,他的节点头是不放数据的。由于是链表结构的所以增删效率比较高,随机查找效率慢(无法通过索引查找存储的值)。另外LinkedLIst**不是线程安全**的,不过可以使用List list=Collections.synchronizedList(new LinkedList(…));的方式使其变成线程安全的。由于LinkedList实现了Queue接口,所以LinkedList不止有队列的接口,还有栈的接口,可以使用LinkedList作为队列和栈的实现。(可以使用在集合头插入元素和在集合尾插入元素模拟队列和栈的数据结构)
 linkedlist的数据存储图

linkedlist的属性解释
transient int size = 0;//linkedlist的集合长度
transient Node first; //集合的第一个存值的节点
transient Node last;//集合的最后一个节点
Node介绍是一个双向列表,他的属性有三个。

    private static class Node<E> {
        E item;//存储的数据
        Node<E> next;//存储的下一个节点
        Node<E> prev;//存储的上一个节点
Node的构造方法
        Node(Node<E> prev, E element, Node<E> next) {//构造有参的方法
            this.item = element;//存储的数据
            this.next = next;//下一个节点
            this.prev = prev;//上一个节点
        }
    }

LinkedList的构造方法有两中
第一 空参的构造方法

   public LinkedList() {//无参的构造方法
    }

第二种传入一个collection集合的实现类

 public LinkedList(Collection<? extends E> c) {//有参的构造方法
        this();
        addAll(c);//调用addall方法
    }

linkedlist的私有方法添加元素到集合头

  private void linkFirst(E e) {
        final Node<E> f = first;//获取集合中第一个存值的节点
        final Node<E> newNode = new Node<>(null, e, f);//创建新节点,将数据存储在新节点中,将first改成当前节点,(因为当在第一次加入元素是first为空,所以linkedlist集合中的头结点不存储数据)
        first = newNode;//修改集合的first为当前节点
        if (f == null)//判断集合是否为空
            last = newNode;//如果为空集合的最后一个节点为当前节点。
        else
            f.prev = newNode;//否则将第二个存值节点的中的prev的值修改为newNode。
        size++;//增加集合的长度
        modCount++;//记录集合修改次数。
    }

linkedlist的默认方法linkLast在集合尾插入元素(鬼知道他怎么不是私有的,百度没找到原因)

    void linkLast(E e) {
        final Node<E> l = last;//获取集合的最后一个节点
        final Node<E> newNode = new Node<>(l, e, null);//创建一个新的节点(因为当在第一次加入元素是last为空,所以linkedlist集合中的头结点不存储数据)
        last = newNode;//将集合的最后一个节点改成当前节点
        if (l == null)//判断最后一个节点是否为空。(也就是集合中之前是否存在元素)
            first = newNode;//如果不存在则这个插入的节点即为第一节点,也为最后一个节点。
        else
            l.next = newNode;//如果之前有节点,将集合的最后一个节点修改成新添加的节点
        size++;//集合的长度加1;
        modCount++;//集合的修改次数加一1;
   }

linkedlist的默认方法linkBefore()在哪个节点前插入元素。

 void linkBefore(E e, Node<E> succ) {//首先保证succ不为空,这个succ是在linkedlist中查找所得,所以必不为空。
        final Node<E> pred = succ.prev;//获得succ的上一个节点
        final Node<E> newNode = new Node<>(pred, e, succ);//创建一个新的节点
        succ.prev = newNode;//修改succ的的上一个元素节点为当前节点
        if (pred == null)//如果要插入的位置在集合头,
            first = newNode;//修改当前节点
        else
            pred.next = newNode;//否则修改插入节点上一个节点的下一个节点为插入节点
        size++;//修改集合节点个数
        modCount++;//修改记录集合修改次数
    }

linklist的私有方法,删除集合中第一个存值的节点

   private E unlinkFirst(Node<E> f) {
        // 出入的节点不为空,即一定为集合中第二个节点
        final E element = f.item;//获取集合中的第二个节点的值
        final Node<E> next = f.next;//获取集合中第二个节点中的下一个节点
        f.item = null;//释放集合中第二个节点存储的值内存,帮助GC回收
        f.next = null; // 释放集合中第二个节点中存储的下一个节点的值
        first = next;//将集合中的第二个节点的值改成传入节点(先集合中的第一个节点)的下一个节点的值。
        if (next == null)//判断如果集合中只有一个节点及及集合中的第一个节点也为最后一个节点
            last = null;//将集合中的最后一个节点设为空。
        else
            next.prev = null;//将先前集合中第一个节点的下一个节点的prev设为空,即将先集合的第三个节点设置成第二个节点
        size--;修改集合中元素个数
        modCount++;//记录集合修改次数
        return element;    //返回移除的节点数据。
}

linklist的私有方法,删除集合中最后一个节点

 private E unlinkLast(Node<E> l) {
        //传入的节点一定为集合中最后一个节点,并且该节点不为空,
        final E element = l.item;//获取传入节点的值
        final Node<E> prev = l.prev;//获取传入节点的上一个节点
        l.item = null;//将传入节点的值清空GC
        l.prev = null; // 将传入节点的上一个节点的值清空帮助GC
        last = prev;//将传入节点的上一节点值设为集合中最后一个节点
        if (prev == null)//判断集合中是否只存在一个节点(即传入的此节点)
            first = null;//如过是将集合中的第一个存值的节点设为空。
        else
            prev.next = null;//否则将本节点的下一节点设为空。
        size--;//修改集合中元素个数
        modCount++;//修改集合修改次数
        return element;//返回清除的节点值
    }

linklist的默认方法,删除集合中的一个节点

 E unlink(Node<E> x) {
        // 传入节点一定不为空
        final E element = x.item;//获取传入节点的值
        final Node<E> next = x.next;//获取传入节点中的下一个节点
        final Node<E> prev = x.prev;//获取传入节点中的上一个节点
        if (prev == null) {//判断传入节点的是否为第一个有值的节点
            first = next;//如果是将集合的第二个存值的节点赋值给集合的first。
        } else {
            prev.next = next;//否则将传入节点的上一个节点中的下一个节点改成传入节点的下一个节点
            x.prev = null;//将传入节点中的上一个节点清空帮助GC
        }
        if (next == null) {//判断当前节点是否为最后一个节点
            last = prev;//如果是将集合中的最后一个节点设为传入节点的上一个节点
        } else {
            next.prev = prev;//否则将传入节点的下一个节点中的上一个节点的值该为传入接待的上一个节点的值。
            x.next = null;//清空传入节点中的下一节指的值帮助GC。
        }

        x.item = null;//清空传入节点的值,
        size--;//集合长度减一
        modCount++;//集合操作加一
        return element;//返回删除节点的值。
    }

判断集合是否存在此指定值,如果存在返回元素在集合中的角标,否则返回-1

 public int indexOf(Object o) {
        int index = 0;//
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

上面是linkedlist的部分内置方法,下面为介绍linkedlist的常用公共方法。

 public E getFirst() {//获取集合中第一个元素
        final Node<E> f = first;//获取集合中的第一个节点
        if (f == null)//如果为空抛异常
            throw new NoSuchElementException();
        return f.item;//返回first中存储的值
    }
  public E getLast() {//获取集合中最后一个元素的值  同上
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }
  public E removeFirst() {//删除集合中first的值
        final Node<E> f = first;//获取集合中first元素
        if (f == null)//判断集合是否为空
            throw new NoSuchElementException();
        return unlinkFirst(f);//调用他的私有方法,删除集合中的第一个存值节点。
    }
 public E removeLast() {//同上
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }

linklist的公共方法,清除集合中所有元素

   public void clear() {
        for (Node<E> x = first; x != null; ) {//遍历集合
            Node<E> next = x.next;//获取当前节点的下一存值节点
            x.item = null;//删除集合中本节点的所有值
            x.next = null;
            x.prev = null;
            x = next;//将X改为下一个存值节点
        }
        first = last = null;
        size = 0;
        modCount++;
    }

linkedlist的迭代器
//判断返回指定位置的节点(此方法 减少了在集合中遍历查找节点的次数。)

   Node<E> node(int index) {
        if (index < (size >> 1)) {//如果传入的迭代的值是远远小于集合长度的
            Node<E> x = first;//获取当前集合中的first节点
            for (int i = 0; i < index; i++)
                x = x.next;//循环返回集合中第index节点的下一个节点,
            return x;//返回当前节点
        } else {
            Node<E> x = last;//如果传入的迭代的值不远远小于集合的长度,则将集合中list的节点赋值给当前节点
            for (int i = size - 1; i > index; i--)//循环返回传入做坐标的节点的的上一个节点
                x = x.prev;
            return x;
        }
    }
  public ListIterator<E> listIterator() {//linkedlist的方法返回一个迭代器,
        return listIterator(0);//传入数组节点的位置创建迭代器
    }
  public ListIterator<E> listIterator(int index) {//迭代器方法,返回一个迭代器
        checkPositionIndex(index);
        return new ListItr(index);
    }

//迭代器的属性

 private Node<E> lastReturned = null;//当前节点元素
        private Node<E> next;//下一个节点元素
        private int nextIndex;//将要获取第几个节点
        private int expectedModCount = modCount;//在获取迭代器时集合修改的次数,在多线程是保证获取数据是否准确安全(不能保证线程安全),
    ListItr(int index) {//迭代器的构造方法,
            next = (index == size) ? null : node(index);//
            nextIndex = index;
        }

迭代器常用的两个方法

     public boolean hasNext() {//判断当前迭代器是否还有值
            return nextIndex < size;
        }
  public E next() {//获取当前迭代器中下一个要获取的值
            checkForComodification();//判断在获取迭代器之后当前集合是否进行增删操作如果有就抛出异常
            if (!hasNext())//判断当前迭代器是否还有元素如果没有则抛出异常
                throw new NoSuchElementException();

            lastReturned = next;//当前迭代值
            next = next.next;//修改迭代器属性next为当前元素的下一个迭代对象
            nextIndex++;//下次获取元素的下标(在集合中链表的位置),
            return lastReturned.item;//返回当前迭代器的值
        }

第一写总结,写的比较乱,都是根据自己的理解写的,所以写的不好。ps(本身都是给我自己看的,能看懂的都是知己啊!!!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值