LinkedList

LinkedList内部是一个链表的实现,一个节点除了保持自身的数据外,还持有前,后两个节点的引用。所以就数据存储上来说,它相比使用数组作为底层数据结构的ArrayList来说,会更加耗费空间。但也正因为这个特性,它删除,插入节点很快!LinkedList没有任何同步手段,所以多线程环境须慎重考虑,可以使用Collections.synchronizedList(new LinkedList(...));保证线程安全

相比于ArrayList,它额外实现了双端队列接口Deque,这个接口主要是声明了队头,队尾的一系列方法。

 

类成员

LinkedList内部有两个引用,一个first,一个last,分别用于指向链表的头和尾,另外有一个size,用于标识这个链表的长度,
而它的接的引用类型是Node,这是他的一个内部类:

item用于保存数据,而prve用于指向当前节点的前一个节点,next用于指向当前节点的下一个节点

/实现Serilizable接口时,将不需要序列化的属性前添加关键字transient,
序列化对象的时候,这个属性就不会序列化到指定的目的地中
transient int size = 0;

    //指向首节点
    transient Node<E> first;
    //指向最后一个节点
    transient Node<E> last;
    
    //内部类,定义了LinkedList内部链表的节点属性
    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;
        }
    }

 add(E e)方法

public boolean add(E e) {
        linkLast(e);
        return true;
    }
 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++;
    }

执行过程,一开始,first和last都为null,此时链表什么都没有,当第一次调用该方法后,first和last均指向了第一个新加的节点E1:

接着,第二次调用该方法,加入新节点E2。首先,将last引用赋值给l,接着new了一个新节点E2,并且E2的prve指向l,接着将新节点E2赋值为last。现在结构如下: 

接着判断l==null? 所以走的else语句,将l的next引用指向新节点E2,现在数据结构如下 

接着size+1,modCount+1,退出该方法,局部变量l销毁,所以现在数据结构如下:

 

add(int index, E element) 这个方法是在指定位置插入新元素

  • index位置检查(不能小于0,大于size)
  • 如果index==size,直接在链表最后插入,相当于调用add(E e)方法
  • 小于size,首先调用node方法将index位置的节点找出,接着调用linkBefore
 public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
 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;
        }
    }
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++;
    }

假设现在链表中有三个节点,调用node方法后找到的第二个节点E2,则进入方法后,结构如下: 

接着,将succ的prev赋值给pred,并且构造新节点E4,E4的prev和next分别为pred和suc,同时将新节点E4赋值为succ的prev引用,则现在结构如下:

接着,将新节点赋值给pred节点的next引用,结构如下

最后,size+1,modCount+1,推出方法,本地变量succ,pred销毁,最后结构如下

这样新节点E4就插入在了第二个E2节点前面。新链表构建完成。从这个过程中我们可以知道,这里并没有大量移动移动以前的元素,所以效率非常高!

E get(int index)获取指定节点数据

 public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
判断index在链表的哪边。
遍历查找index或者size-index次,找出对应节点。
这里我们知道,相比于数组的直接索引获取,遍历获取节点效率并不高
 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;
        }
    }

 E remove(int index)移除指定节点

检查index位置
调用node方法获取节点,接着调用unlink(E e) 
public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
将当前节点X的前一个节点P的next直接指向X的下一个节点D,
这样X就不再关联任何引用,等待垃圾回收即可。
相比于ArrayList的copy数组覆盖原来节点,效率同样更高!

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;
        } else {
            prev.next = next;
            x.prev = null;
        }

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

        x.item = null;
        size--;
        modCount++;
        return element;
    }
//将节点值为e的节点作为首节点
   private void linkFirst(E e) {
        final Node<E> f = first;
        //构建一个prev值为null,next为f,节点值为e的节点
        final Node<E> newNode = new Node<>(null, e, f);
        //将newNode作为首节点
        first = newNode;
        //如果newNode后面没有节点就将newNode作为最后一个节点
        if (f == null)
            last = newNode;
        //否则就将newNode赋给其prev
        else
            f.prev = newNode;
        //列表长度加一
        size++;
        modCount++;
    }
    //将节点值为e的节点作为最后一个节点
    void linkLast(E e) {
        final Node<E> l = last;
        //构建一个prev值为l,next为null,节点值为e的节点
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    //在非空节点succ之前插入节点e
    void linkBefore(E e, Node<E> succ) {
        final Node<E> pred = succ.prev;
        //将succ前面的节点和succ作为其prev和next
        final Node<E> newNode = new Node<>(pred, e, succ);
        //然后将newNode作为succ的prev
        succ.prev = newNode; 
        //如果原来succ是首节点,则将newNode设置为首节点
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }
    //释放非空的首节点f
    private E unlinkFirst(Node<E> f) {
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        //将first节点后面的节点设为首节点
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }
    //释放非空的尾节点l
    private E unlinkLast(Node<E> l) {
        final E element = l.item;
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }
    //删除非空节点x
    E unlink(Node<E> x) 
    {
        final E element = x.item;
        final Node<E> next = x.next;    //x后面的节点
        final Node<E> prev = x.prev;    //x前面的节点

         if (prev == null) {
             first = next;
         } else {
             prev.next = next;
             x.prev = null;
         }
         if (next == null) {
             last = prev;
         } else {
             next.prev = prev;
             x.next = null;
         }
         x.item = null;
         size--;
         modCount++;
         return element;
     }
     //返回列表首节点元素值
     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;
     }
     //移除首节点
     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);
     }
   //在列表首部插入节点e
     public void addFirst(E e) {
         linkFirst(e);
     }
     //在列表尾部插入节点e
     public void addLast(E e) {
         linkLast(e);
     }
    //判断列表中是否包含有元素o
     public boolean contains(Object o) {
         return indexOf(o) != -1;
     }
     //返回列表长度大小
     public int size() {
         return size;
     }
     //在列表尾部插入元素
     public boolean add(E e) {
         linkLast(e);
         return true;
     }
     //删除元素为o的节点
     public boolean remove(Object o) 
     {    
         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;
     }
    //将集合c中所有元素添加到列表的尾部
     public boolean addAll(Collection<? extends E> c) {
         return addAll(size, c);
     }
    //从指定的位置index开始,将集合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 {  //否则,找到index所在的节点
             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;
     }
     //删除列表中所有节点
     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;
         modCount++;
     }
     //获取指定索引位置节点的元素值
     public E get(int index) {
         checkElementIndex(index);
         return node(index).item;
     }
     //替换指定索引位置节点的元素值
     public E set(int index, E element) {
         checkElementIndex(index);
         Node<E> x = node(index);
         E oldVal = x.item;
         x.item = element;
         return oldVal;
     }
     //在指定索引位置之前插入元素e
     public void add(int index, E element) {
         checkPositionIndex(index);   
         if (index == size)
             linkLast(element);
         else
             linkBefore(element, node(index));
     }
     //删除指定位置的元素
     public E remove(int index) {
         checkElementIndex(index);
         return unlink(node(index));
     }
  //返回指定索引位置的节点
  Node<E> node(int index) {
    //此处是一个小技巧,当index<size/2时,从列表前半部分开始,否则从后半部分开始
    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;
    }
  }

  //返回列表中第一次出现o的位置,若不存在则返回-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;
  }
  //逆向搜索,返回第一出现o的位置,不存在则返回-1
  public int lastIndexOf(Object o) {
    int index = size;
    if (o == null) {
      for (Node<E> x = last; x != null; x = x.prev) {
        index--;
        if (x.item == null)
          return index;
      }
    } else {
      for (Node<E> x = last; x != null; x = x.prev) {
        index--;
        if (o.equals(x.item))
          return index;
      }
    }
    return -1;
  }

 

队列Deque

 

  • add(E e):队尾插入新节点,如果队列空间不足,抛出异常;LinkedList没有空间限制,所以可以无限添加。
  • offer(E e):队尾插入新节点,空间不足,返回false,在LinkedList中和add方法同样效果。
  • remove():移除队头节点,如果队列为空(没有节点,first为null),抛出异常。LinkedList中就是first节点(链表头)
  • poll():同remove,不同点:队列为空,返回null
  • element():查询队头节点(不移除),如果队列为空,抛出异常。
  • peek():同element,不同点:队列为空,返回null。

总结

  1. LinkedList内部使用链表实现,相比于ArrayList更加耗费空间。
  2. LinkedList插入,删除节点不用大量copy原来元素,效率更高。
  3. LinkedList查找元素使用遍历,效率一般。
  4. LinkedList同时是双向队列的实现

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值