LinkedList(jdk1.8)源码分析

概述

LinkedList实现了List接口,在之前使用ArrayList的地方可以替换成LinkedList直接使用。与ArrayList相比,LinkedList(没有实现RandomAccess接口)底层使用链表实现数据的存储,链表相对于数组,在查找获取方面不能直接通过索引获取,只能遍历获取,速度较慢。但相对于数组实现的ArrayList在插入和删除数据的时候,不存在容量扩容,也不需要数据的copy平移,速度上ArrayList快。
LinkedList 实现了Deque接口,Deque为一个双端队列接口,定义了队列,双端队列,栈操作的相关方法。也就是LinkedList可以用作队列,和栈容器。

类继承关系图:

这里写图片描述
LinkedList 实现了Iterable接口,说明该类可以通过迭代器遍历他。
LinkedList 实现了Collection接口,说明该类具有集合常规方法操作。
LinkedList 实现了List接口,说明该类具有线性表操作方法。
LinkedList实现了Queue/Deque接口,说明该类可以用作栈,队列,和双端队列。
LinkedList实现了Cloneable接口,说明该类具有clone方法。
LinkedList实现了Serializable接口,说明该类可以进行序列化。

成员变量:
//容器中元素个数
transient int size = 0;
//链表头部节点(指针)
transient Node<E> first;
//链表尾部节点(指针)
transient Node<E> last;
节点内部类:
 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;
        }
    }
构造方法:
 //默认构造方法,构造一个空的LinkedList
    public LinkedList() {
    }

 //构造一个包含指定集合元素的LinkedList
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
List接口相关方法:

新增元素

 //在链表尾部加入一个元素
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
  //在链表尾部加入一个node元素
    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;  //链表容量为空的时候加入一个元素 //first==last==newNode
        else
            l.next = newNode;//链表不为空,则直接链接到last元素后边
        //容量加一
        size++;
        //修改modCount 用于迭代器遍历
        modCount++;
    }
   //在链表指定接点加入一个node元素
    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;//succ前节点为空,则将first节点赋值newNode
        else
            pred.next = newNode;//succ前节点不为空则直接将新加节点链接到前节点的后继节点
        size++;//容量加一
        modCount++;//修改modCount 用于迭代器遍历
    }
    //指定添加一个元素
    public void add(int index, E element) {
    //校验index是否越界
        checkPositionIndex(index);
        if (index == size)
            linkLast(element);//index为最后一个元素,链接到链表尾部
        else
            linkBefore(element, node(index));//index不为最后一个元素,则将新元素链接到该指定位置
    }
    //校验索引是否越界,越界抛出异常
    private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    //判断索引位置是否合法
    boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }
//添加指定集合中的元素到LinkedList
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, 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;//pred指向当前最新节点元素
        }

        if (succ == null) {//index插入位置为链表尾部则last指向最后一个新增元素节点
            last = pred;
        } else {//index插入位置为链表其他位置,则pred后继节点指向断开节点
            pred.next = succ;
            succ.prev = pred; 断开节点前继节点指向添加元素位置
        }

        size += numNew;//LinkedList数量增加numNew 
        modCount++;//修改modCount 用于迭代器遍历
        return true;
    }

删除元素

//删除指定元素
 public boolean remove(Object o) {
 //LinkedList允许元素为NULL
        if (o == null) {
        //遍历寻找某个元素
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                //从链表中移除某个元素
                    unlink(x);
                    return true;
                }
            }
        } else {
        //遍历寻找某个元素
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                //从链表中移除某个元素
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
    //从链表中移除某个节点
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
        //处理移除节点的前继节点
        if (prev == null) {
            first = next;//移除节点为链表头部,则头节点直接指向next节点
        } else {
            prev.next = next;//移除节点不为链表头部,则移除节点的前继节点的后继节点指向next节点
            x.prev = null;//将移除节点的前继节点置NULL
        }
        //处理移除节点的后继节点
        if (next == null) {//移除节点为链表尾部,则将尾部节点直接指向移除节点的前继节点
            last = prev;
        } else {//移除节点不为链表尾部,则移除节点的后继节点的前继节点指向移除节点的后继节点
            next.prev = prev;
            x.next = null;//将移除节点的后继节点置NULL
        }
        //移除节点数据端置NULL便于垃圾回收
        x.item = null;
        size--;//数量减一
        modCount++;//修改modCount值
        return element;
    }
    //清空LinkedList
   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;
        }
        first = last = null;//头部指针和尾部指针置空
        size = 0;//LinkedList大小置空
        modCount++;//修改modCount值
    }

查找元素

  //获取指定位置元素
    public E get(int index) {
    //校验索引是否合法
        checkElementIndex(index);
        //调用node方法获取元素数据
        return node(index).item;
    }
   //检验索引是否在合理范围,不合理抛出IndexOutOfBoundsException异常
   private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    //真正校验索引范围
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }
  //获取指定索引位置节点方法
    Node<E> node(int index) {
        // assert isElementIndex(index);
        //判断索引的位置在容器前半部分还是后半部分
       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;
        }
    }

修改元素

//修改指定位置元素
 public E set(int index, E element) {
        //校验索引位置是否合法
        checkElementIndex(index);
        //获取指定索引节点
        Node<E> x = node(index);
        //节点数据赋值
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

迭代器

  //获取指定范围的迭代器,且可以正向和反向迭代遍历
    public ListIterator<E> listIterator(int index) {
    //校验索引是否在合法范围
        checkPositionIndex(index);
        return new ListItr(index);
    }
//新版本迭代器内部类
    private class ListItr implements ListIterator<E> {
    //迭代遍历返回的当前临时节点(next方法中使用,用于缓存当前节点),初始值为null,迭代器迭代遍历开始后指向容器当前节点值。
        private Node<E> lastReturned;
        //当前节点 next方法调用后指向下一个节点
        private Node<E> next;
        //当前节点的索引值,next方法调用后指向下一个节点索引值,用于判断是否还有数据
        private int nextIndex;
        //迭代器遍历时候判断容器是否发生过变化
        private int expectedModCount = modCount;
//指定索引位置的构造方法
        ListItr(int index) {
            // assert isPositionIndex(index);
            //index==size 则next为空,表示迭代器处于遍历结束状态,否则等于node方法返回值,node方法内部有index越界判断,越界则抛出异常
            next = (index == size) ? null : node(index);
            //当前节点索引值,next方法调用后指向下一个节点索引值
            nextIndex = index;
        }
//判断正向遍历是否还有元素
        public boolean hasNext() {
            return nextIndex < size;
        }
//正向迭代遍历取值
        public E next() {
        //校验LinkedList是否修改过 若修改过抛出异常
            checkForComodification();
            //若正向遍历没有值则抛异常
            if (!hasNext())
                throw new NoSuchElementException();
//缓存当前节点引用,用于节点元素值返回
            lastReturned = next;
            //next节点指向下个节点
            next = next.next;
            //nextIndex索引指向下个节点索引
            nextIndex++;
            //返回当前节点数据元素
            return lastReturned.item;
        }
//是否可以进行前向迭代
        public boolean hasPrevious() {
            return nextIndex > 0;
        }
//前向遍历取值
        public E previous() {
         //校验LinkedList是否修改过,若修改过抛出异常
            checkForComodification();
            //若前向遍历无值,抛出异常
            if (!hasPrevious())
                throw new NoSuchElementException();
//缓存当前节点引用,用于节点元素值返回。此外对next赋值next的前驱节点,若next==null,
//则赋值当前容器的尾部节点(本人分析代码逻辑不会赋值尾部节点)
            lastReturned = next = (next == null) ? last : next.prev;
            //nextIndex索引指向前向节点索引
            nextIndex--;
            //返回当前值
            return lastReturned.item;
        }
//获取当前节点索引值
        public int nextIndex() {
            return nextIndex;
        }
//获取前向节点索引值
        public int previousIndex() {
            return nextIndex - 1;
        }
//通过迭代器移除索引指向元素
        public void remove() {
        //校验容器状态,是否修改过
            checkForComodification();
            //校验当前迭代器节点值,没有开始迭代,移除元素抛出异常
            if (lastReturned == null)
                throw new IllegalStateException();
//临时缓存迭代器当前节点的下一个节点
            Node<E> lastNext = lastReturned.next;
            //移除当前节点,调用容器的删除方法()
            unlink(lastReturned);
            //处理迭代器状态(next==lastReturned逻辑,比较绕,发现调用next方法后,再调用previous方法,会出现此种逻辑,两者反向调用也会出现此种情况),
            if (next == lastReturned)
                next = lastNext;
            else
                nextIndex--;
            lastReturned = null;//移除当前节点后,lastRetunrned置空,垃圾回收
            expectedModCount++;//修改容器状态变化标志,标识容器发生了变化
        }
//通过迭代器更新元素
        public void set(E e) {
            if (lastReturned == null)
                throw new IllegalStateException();
            checkForComodification();
            //直接赋值
            lastReturned.item = e;
        }
//通过迭代器新增元素
        public void add(E e) {
        //校验容器状态发生变化
            checkForComodification();
            //当前返回节点值置空
            lastReturned = null;
            //此处逻辑和LinkedList容器新增逻辑相同
            if (next == null)
                linkLast(e);//当前遍历器处于链表尾部,直接向链表尾部插入数据
            else
                linkBefore(e, next);//迭代器处于链表其他位置,则向指定节点前插入新节点
            nextIndex++;//迭代器索引指向next所指向数据
            expectedModCount++;//修改容器变化状态标志,标识容器发生了变化
        }
//jdk1.8新加 jdk lambda表达式
        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            while (modCount == expectedModCount && nextIndex < size) {
                action.accept(next.item);
                lastReturned = next;
                next = next.next;
                nextIndex++;
            }
            checkForComodification();
        }
//校验LinkedList是否发生变化(新增元素,删除元素)
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

   //获取老版本迭代器
    public Iterator<E> descendingIterator() {
        return new DescendingIterator();
    }

 //兼容1.6之前版本的老版本迭代器 该迭代器,就是对新迭代器的包装,只能进行正向遍历
    private class DescendingIterator implements Iterator<E> {
        private final ListItr itr = new ListItr(size());
        public boolean hasNext() {
            return itr.hasPrevious();
        }
        public E next() {
            return itr.previous();
        }
        public void remove() {
            itr.remove();
        }
    }
其它方法
//链表转化为数组
    public Object[] toArray() {
    //new 一个新数组
        Object[] result = new Object[size];
        int i = 0;
        //遍历将数据copy到数组
        for (Node<E> x = first; x != null; x = x.next)
            result[i++] = x.item;
        return result;
    }
//序列化标志Id
    private static final long serialVersionUID = 876323262645176354L;
  //自定义序列化方法
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden serialization magic
        s.defaultWriteObject();

        // Write out size
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (Node<E> x = first; x != null; x = x.next)
            s.writeObject(x.item);
    }

 //自定义反序列化方法
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        s.defaultReadObject();

        // Read in size
        int size = s.readInt();

        // Read in all elements in the proper order.
        for (int i = 0; i < size; i++)
            linkLast((E)s.readObject());
    }
栈操作接口方法
//元素进栈方法
    public void push(E e) {
        addFirst(e);
    }
//在链表头部加入新节点
    public void addFirst(E e) {
        linkFirst(e);
    }

 //真正链表头部加入节点方法
    private void linkFirst(E e) {
        final Node<E> f = first;
        //构造新加入元素节点,新节点后继节点指向f节点,前继节点为null
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
       //当容器为空的时候,last尾部节点==first==newNode
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;//之前的头结点的前继节点指向新加入节点
        size++;//修改容器当前数量
        modCount++;//累加容器修改状态值
    }
 //元素出栈方法
    public E pop() {
        return removeFirst();
    }
   //从链表头部取头节点
    public E removeFirst() {
        final Node<E> f = first;
        //当头节点为空也就是容器为空的时候,抛出异常
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);//从链表中去除节点
    }
   //从链中移除除节点方法
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        //缓存节点元素
        final E element = f.item;
        //缓存当前节点
        final Node<E> next = f.next;
        //元素置空
        f.item = null;
        //后继节点置空
        f.next = null; // help GC
        //头节点指向next节点
        first = next;
        if (next == null)
            last = null;//最后一个元素出栈,会走此逻辑
        else
            next.prev = null;//前继节点置空
            //(first节点的前继节点都为空)
        size--;//容量减一
        modCount++;//累加,容器修改状态
        return element;//返回出栈元素
    }
队列操作接口方法
//向队列中加入元素
    public boolean offer(E e) {
    //内部调用add方法
        return add(e);
    }
    //向队列加入元素此为list接口定义方法
     public boolean add(E e) {
      //在链表尾部加入一个元素
        linkLast(e);
        return true;
    }
 //在链表尾部加入一个元素
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
  //在链表尾部加入一个node元素
    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;  //链表容量为空的时候加入一个元素 //first==last==newNode
        else
            l.next = newNode;//链表不为空,则直接链接到last元素后边
        //容量加一
        size++;
        //修改modCount 用于迭代器遍历
        modCount++;
    }

//从队列头部获取一个元素,并移除,当队列为空会抛出异常
   public E remove() {
  //内部调用removeFirst
        return removeFirst();
    }
    //移除链表第一个节点
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }
    //从链表中移除头节点,并返回头节点元素
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }
 //该函数和remove函数功能基本一致,只是当队列为空,返回null值
  public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }
//从队列中获取第一个元素,但不移除该元素,当队列为空返回null值
    public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }
//从队列中获取第一个元素,但不移除该元素,当队列为空抛出异常
    public E element() {
        return getFirst();
    }
 //真正的获取第一个元素
      public E getFirst() {
        final Node<E> f = first;
        //队列会空抛出异常
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }
双端队列接口方法
 //向双端队列中头部加入一个元素
public void addFirst(E e) {
        linkFirst(e);//链表头部加入节点
   }
//向双端队列尾部加入一个元素
 public void addLast(E e) {
        linkLast(e);//链表尾部加入节点
    }
 //向双端队列中头部加入一个元素,和Addfirst方法一样只是加入了返回值
 public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }
 //向双端队列中头部加入一个元素,和addLast方法一样只是加入了返回值
 public boolean offerLast(E e) {
        addLast(e);
        return true;
    }
 //从双端队列头部获取一个元素并移除该元素,当队列为空抛出异常
  public E removeFirst() {
        final Node<E> f = 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);
    }
//从双端队列头部获取一个元素,并移除该元素,当队列为空返回null
  public E pollFirst() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }
//从双端队列尾部获取一个元素,并移除该元素,当队列为空返回null
  public E pollLast() {
        final Node<E> l = last;
        return (l == null) ? null : unlinkLast(l);
    }
//从队列头部获取元素,不移除该元素,队列为空抛出异常
   public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }
//从队列尾部获取元素,不移除该元素,队列为空抛出异常
  public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }
//从队列头部获取元素,不移除该元素,队列为空返回NULL
    public E peekFirst() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
     }
//从队列尾部部获取元素,不移除该元素,队列为空返回NULL
    public E peekLast() {
        final Node<E> l = last;
        return (l == null) ? null : l.item;
    }

从双端队列中移除某个元素,正向向遍历链表删除
public boolean removeFirstOccurrence(Object o) {
        return remove(o);
    }

//从双端队列中移除某个元素,反向遍历链表删除
  public boolean removeLastOccurrence(Object o) {
        if (o == null) {
            for (Node<E> x = last; x != null; x = x.prev) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = last; x != null; x = x.prev) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
总结:

LinkedList底层由双向链表实现,可以轻松实现数据插入,和删除(比数组实现的ArrayList 效率高),不需要扩容,但查找访问效率比数组慢。
LinkedList实现了List接口,在使用ArrayList的地方可以替换使用。
LinkedList实现了Deque接口,可以用作队列,栈,双端队列。
LinkedList 不支持多线程,通过迭代器遍历的时候,如果有其他线程删除或新增元素,会抛出 ConcurrentModificationException()异常。
LinkedList 遍历删除元素,最好通过迭代器实现。LinkedList有两种迭代器,一种只能单向迭代,最新的一种可以双向,并且指定迭代范围。
LinkedList实现了自己的序列化方法。

问题: vant2 van-popup不能弹出 回答: 如果vant2的van-popup无法弹出,可能是由于以下几个原因导致的。首先,你需要确保在打开前关闭键盘,可以使用以下代码实现: ```html <van-popup class="dialog" show="{{ show }}" bind:before-enter="hideKeyboard"></van-popup> ``` ```javascript hideKeyboard() { wx.hideKeyboard({ complete: res => { console.log('hideKeyboard', res) } }) } ``` 其次,如果白色区域有overflow-y:auto,图片定位右上角不显示,你可以尝试以下代码: ```html <van-popup class="dialog" show="{{ show }}"> <view class="pop"> <van-icon class="close" name="close" bindtap="close"/> <image class="img" src="xxxx.png" lazy-load="true"></image> </view> </van-popup> ``` ```css .pop { position: relative; padding: 64rpx 75rpx; } .close { position: absolute; top: -58rpx; right: -46rpx; z-index: 999; } .img { height: 300rpx; width: 300rpx; } ``` 最后,如果你想要实现弹窗滚动同时底部滚动,可以使用以下代码: ```html <view wx:for="{{100}}" wx:key="item">{{item}}</view> <van-popup show="{{ true }}" custom-style="height: 20%;width: 80%" catchtouchmove="{{ catchMove }}">内容</van-popup> ``` ```javascript data:{ catchMove: true } ``` 希望以上解决方法能够帮助到你解决vant2 van-popup无法弹出的问题。 #### 引用[.reference_title] - *1* *2* *3* [【Vant Weapp】van-popup 弹出层](https://blog.csdn.net/wuli_youhouli/article/details/127763074)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值