zoukankan      html  css  js  c++  java
  • Java集合之LinkedList源码分析

    一、LinkedList简介

      LinkedList是一种可以在任何位置进行高效地插入和移除操作的有序序列,它是基于双向链表实现的。

      ps:这里有一个问题,就是关于实现LinkedList的数据结构是否为循环的双向链表,上网搜了有很多文章都说是循环的,并且有的文章中但是我看了源代码觉得应该不是循环的?

      例如在删除列表尾部节点的代码:

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

      这里删除尾节点l后,将l前面的节点prev的next置为null,而并没有指向head节点。不知道是不是因为代码版本的原因(我的源代码是在下载的jdk1.8.0_45文件中获取的),如果读者看到知道原因,希望能够帮忙解答!

      在源码中定义了节点的基本结构:

     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的类图如下所示:

    LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
    LinkedList 实现 List 接口,能对它进行队列操作。
    LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
    LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
    LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
    LinkedList 是非同步的。

    二、LinkedList源码分析

      1 public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
      2 {
      3    //实现Serilizable接口时,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
      4     transient int size = 0;
      5     //指向首节点
      6     transient Node<E> first;
      7     //指向最后一个节点
      8     transient Node<E> last;
      9     //构建一个空列表
     10     public LinkedList() {
     11     }
     12     //构建一个包含集合c的列表
     13     public LinkedList(Collection<? extends E> c) {
     14         this();
     15         addAll(c);
     16     }
     17    //将节点值为e的节点作为首节点
     18     private void linkFirst(E e) {
     19         final Node<E> f = first;
     20         //构建一个prev值为null,next为f,节点值为e的节点
     21         final Node<E> newNode = new Node<>(null, e, f);
     22         //将newNode作为首节点
     23         first = newNode;
     24         //如果newNode后面没有节点就将newNode作为最后一个节点
     25         if (f == null)
     26             last = newNode;
     27         //否则就将newNode赋给其prev
     28         else
     29             f.prev = newNode;
     30         //列表长度加一
     31         size++;
     32         modCount++;
     33     }
     34     //将节点值为e的节点作为最后一个节点
     35     void linkLast(E e) {
     36         final Node<E> l = last;
     37       //构建一个prev值为l,next为null,节点值为e的节点
     38         final Node<E> newNode = new Node<>(l, e, null);
     39         last = newNode;
     40         if (l == null)
     41             first = newNode;
     42         else
     43             l.next = newNode;
     44         size++;
     45         modCount++;
     46     }
     47     //在非空节点succ之前插入节点e
     48     void linkBefore(E e, Node<E> succ) {
     49         final Node<E> pred = succ.prev;
     50         //将succ前面的节点和succ作为其prev和next
     51         final Node<E> newNode = new Node<>(pred, e, succ);
     52         //然后将newNode作为succ的prev
     53         succ.prev = newNode; 
     54         //如果原来succ是首节点,则将newNode设置为首节点
     55         if (pred == null)
     56             first = newNode;
     57         else
     58             pred.next = newNode;
     59         size++;
     60         modCount++;
     61     }
     62      //释放非空的首节点f
     63     private E unlinkFirst(Node<E> f) {
     64         final E element = f.item;
     65         final Node<E> next = f.next;
     66         f.item = null;
     67         f.next = null; // help GC
     68         //将first节点后面的节点设为首节点
     69         first = next;
     70         if (next == null)
     71             last = null;
     72         else
     73             next.prev = null;
     74         size--;
     75         modCount++;
     76         return element;
     77     }
     78      //释放非空的尾节点l
     79        private E unlinkLast(Node<E> l) {
     80         final E element = l.item;
     81         final Node<E> prev = l.prev;
     82         l.item = null;
     83         l.prev = null; // help GC
     84         last = prev;
     85         if (prev == null)
     86             first = null;
     87         else
     88             prev.next = null;
     89         size--;
     90         modCount++;
     91         return element;
     92     }
     93     //删除非空节点x
     94     E unlink(Node<E> x) 
     95     {
     96         final E element = x.item;
     97         final Node<E> next = x.next;    //x后面的节点
     98         final Node<E> prev = x.prev;    //x前面的节点
     99 
    100         if (prev == null) {
    101             first = next;
    102         } else {
    103             prev.next = next;
    104             x.prev = null;
    105         }
    106         if (next == null) {
    107             last = prev;
    108         } else {
    109             next.prev = prev;
    110             x.next = null;
    111         }
    112         x.item = null;
    113         size--;
    114         modCount++;
    115         return element;
    116     }
    117     //返回列表首节点元素值
    118     public E getFirst() {
    119         final Node<E> f = first;
    120         if (f == null)
    121             throw new NoSuchElementException();
    122         return f.item;
    123     }
    124     //返列表尾节点元素值
    125     public E getLast() {
    126         final Node<E> l = last;
    127         if (l == null)
    128             throw new NoSuchElementException();
    129         return l.item;
    130     }
    131     //移除首节点
    132     public E removeFirst() {
    133         final Node<E> f = first;
    134         if (f == null)
    135             throw new NoSuchElementException();
    136         return unlinkFirst(f);
    137     }
    138    //删除尾节点
    139     public E removeLast() {
    140         final Node<E> l = last;
    141         if (l == null)
    142             throw new NoSuchElementException();
    143         return unlinkLast(l);
    144     }
    145   //在列表首部插入节点e
    146     public void addFirst(E e) {
    147         linkFirst(e);
    148     }
    149     //在列表尾部插入节点e
    150     public void addLast(E e) {
    151         linkLast(e);
    152     }
    153    //判断列表中是否包含有元素o
    154     public boolean contains(Object o) {
    155         return indexOf(o) != -1;
    156     }
    157     //返回列表长度大小
    158     public int size() {
    159         return size;
    160     }
    161     //在列表尾部插入元素
    162     public boolean add(E e) {
    163         linkLast(e);
    164         return true;
    165     }
    166     //删除元素为o的节点
    167     public boolean remove(Object o) 
    168     {    
    169         if (o == null) {
    170             for (Node<E> x = first; x != null; x = x.next) {
    171                 if (x.item == null) {
    172                     unlink(x);
    173                     return true;
    174                 }
    175             }
    176         } else {
    177             for (Node<E> x = first; x != null; x = x.next) {
    178                 if (o.equals(x.item)) {
    179                     unlink(x);
    180                     return true;
    181                 }
    182             }
    183         }
    184         return false;
    185     }
    186    //将集合c中所有元素添加到列表的尾部
    187     public boolean addAll(Collection<? extends E> c) {
    188         return addAll(size, c);
    189     }
    190    //从指定的位置index开始,将集合c中的元素插入到列表中
    191     public boolean addAll(int index, Collection<? extends E> c) {
    192         //首先判断插入位置的合法性
    193         checkPositionIndex(index);
    194         Object[] a = c.toArray();
    195         int numNew = a.length;
    196         if (numNew == 0)
    197             return false;
    198         Node<E> pred, succ;
    199         if (index == size) {//说明在列表尾部插入集合元素
    200             succ = null;
    201             pred = last;
    202         } 
    203         else {  //否则,找到index所在的节点
    204             succ = node(index);
    205             pred = succ.prev;
    206         }
    207         for (Object o : a) {
    208             @SuppressWarnings("unchecked") E e = (E) o;
    209             Node<E> newNode = new Node<>(pred, e, null);
    210             if (pred == null)
    211                 first = newNode;
    212             else
    213                 pred.next = newNode;
    214             pred = newNode;
    215         }
    216         if (succ == null) {
    217             last = pred;
    218         } else {
    219             pred.next = succ;
    220             succ.prev = pred;
    221         }
    222         size += numNew;
    223         modCount++;
    224         return true;
    225     }
    226     //删除列表中所有节点
    227     public void clear() {
    228         for (Node<E> x = first; x != null; )  
    229         {
    230             Node<E> next = x.next;
    231             x.item = null;
    232             x.next = null;
    233             x.prev = null;
    234             x = next;
    235         }
    236         first = last = null;
    237         size = 0;
    238         modCount++;
    239     }
    240     //获取指定索引位置节点的元素值
    241     public E get(int index) {
    242         checkElementIndex(index);
    243         return node(index).item;
    244     }
    245     //替换指定索引位置节点的元素值
    246     public E set(int index, E element) {
    247         checkElementIndex(index);
    248         Node<E> x = node(index);
    249         E oldVal = x.item;
    250         x.item = element;
    251         return oldVal;
    252     }
    253     //在指定索引位置之前插入元素e
    254     public void add(int index, E element) {
    255         checkPositionIndex(index);   
    256         if (index == size)
    257             linkLast(element);
    258         else
    259             linkBefore(element, node(index));
    260     }
    261     //删除指定位置的元素
    262     public E remove(int index) {
    263         checkElementIndex(index);
    264         return unlink(node(index));
    265     }
    266     //判断指定索引位置的元素是否存在
    267     private boolean isElementIndex(int index) {
    268         return index >= 0 && index < size;
    269     }
    270     private boolean isPositionIndex(int index) {
    271         return index >= 0 && index <= size;
    272     }
    273     //构建 IndexOutOfBoundsException详细信息
    274     private String outOfBoundsMsg(int index) {
    275         return "Index: "+index+", Size: "+size;
    276     }
    277     private void checkElementIndex(int index) {
    278         if (!isElementIndex(index))
    279             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    280     }
    281     private void checkPositionIndex(int index) {
    282         if (!isPositionIndex(index))
    283             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    284     }
    285     //返回指定索引位置的节点
    286     Node<E> node(int index) {
    287         //此处是一个小技巧,当index<size/2时,从列表前半部分开始,否则从后半部分开始
    288         if (index < (size >> 1)) {
    289             Node<E> x = first;
    290             for (int i = 0; i < index; i++)
    291                 x = x.next;
    292             return x;
    293         } else {
    294             Node<E> x = last;
    295             for (int i = size - 1; i > index; i--)
    296                 x = x.prev;
    297             return x;
    298         }
    299     }//返回列表中第一次出现o的位置,若不存在则返回-1
    300     public int indexOf(Object o) {
    301         int index = 0;
    302         if (o == null) {
    303             for (Node<E> x = first; x != null; x = x.next) {
    304                 if (x.item == null)
    305                     return index;
    306                 index++;
    307             }
    308         } else {
    309             for (Node<E> x = first; x != null; x = x.next) {
    310                 if (o.equals(x.item))
    311                     return index;
    312                 index++;
    313             }
    314         }
    315         return -1;
    316     }
    317     //逆向搜索,返回第一出现o的位置,不存在则返回-1
    318     public int lastIndexOf(Object o) {
    319         int index = size;
    320         if (o == null) {
    321             for (Node<E> x = last; x != null; x = x.prev) {
    322                 index--;
    323                 if (x.item == null)
    324                     return index;
    325             }
    326         } else {
    327             for (Node<E> x = last; x != null; x = x.prev) {
    328                 index--;
    329                 if (o.equals(x.item))
    330                     return index;
    331             }
    332         }
    333         return -1;
    334     }
    335    //获取列表首节点元素值
    336     public E peek() {
    337         final Node<E> f = first;
    338         return (f == null) ? null : f.item;
    339     }
    340 
    341     //获取列表首节点元素值,若为空则抛异常
    342     public E element() {
    343         return getFirst();
    344     }
    345    //检索首节点,若空则返回null,不为空则返回其元素值并删除首节点
    346     public E poll() {
    347         final Node<E> f = first;
    348         return (f == null) ? null : unlinkFirst(f);
    349     }
    350     //检索首节点,若空则抛异常,不为空则返回其元素值并删除首节点
    351     public E remove() {
    352         return removeFirst();
    353     }
    354    //在列表尾部增加节点e
    355     public boolean offer(E e) {
    356         return add(e);
    357     }
    358    //在列表首部插入节点e
    359     public boolean offerFirst(E e) {
    360         addFirst(e);
    361         return true;
    362     }
    363   //在列表尾部插入节点e
    364     public boolean offerLast(E e) {
    365         addLast(e);
    366         return true;
    367     }
    368     public E peekFirst() {
    369         final Node<E> f = first;
    370         return (f == null) ? null : f.item;
    371      }
    372   //获取列表尾节点元素值
    373     public E peekLast() {
    374         final Node<E> l = last;
    375         return (l == null) ? null : l.item;
    376     }
    377     public E pollFirst() {
    378         final Node<E> f = first;
    379         return (f == null) ? null : unlinkFirst(f);
    380     }
    381     public E pollLast() {
    382         final Node<E> l = last;
    383         return (l == null) ? null : unlinkLast(l);
    384     }
    385    //入栈
    386     public void push(E e)
    387     {
    388         addFirst(e);
    389     }
    390     //出栈
    391     public E pop() {
    392         return removeFirst();
    393     }
    394     //删除列表中第一出现o的节点
    395     public boolean removeFirstOccurrence(Object o) {
    396         return remove(o);
    397     }
    398     //逆向搜索,删除第一次出现o的节点
    399     public boolean removeLastOccurrence(Object o) {
    400         if (o == null) {
    401             for (Node<E> x = last; x != null; x = x.prev) {
    402                 if (x.item == null) {
    403                     unlink(x);
    404                     return true;
    405                 }
    406             }
    407         } else {
    408             for (Node<E> x = last; x != null; x = x.prev) {
    409                 if (o.equals(x.item)) {
    410                     unlink(x);
    411                     return true;
    412                 }
    413             }
    414         }
    415         return false;
    416     }

    三、关于LinkedList的几点说明

      1、注意源码中的 Node<E> node(int index)方法:

        Node<E> node(int 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;
            }
        }                   

      该方法返回双向链表中指定位置处的节点,而链表中是没有下标索引的,要指定位置出的元素,就要遍历该链表,从源码的实现中,我们看到这里有一个加速动作。源码中先将index与长度size的一半比较,如果index<size/2,就只从位置0往后遍历到位置index处,而如果index>size/2,就只从位置size往前遍历到位置index处。这样可以减少一部分不必要的遍历。

      2、LinkedList与ArrayList的区别:

      LinkedList与ArrayList在性能上各有优缺点,都有各自适用的地方,总结如下:

    • ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
    • LinkedList不支持高效的随机元素访问。
    • ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间,就存储密度来说,ArrayList是优于LinkedList的。  

      总之,当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能,当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。

      3、LinkedList中允许元素为null

      在查找和删除时,源代码如下所示:

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

      4、利用LinkedList实现栈操作

    public class Stack<T>
    {
        private LinkedList<T> stack;
        
        //无参构造函数
        public Stack()
        {
            stack=new LinkedList<T>();
        }
        //构造一个包含指定collection中所有元素的栈
        public Stack(Collection<? extends T> c)
        {
            stack=new LinkedList<T>(c);
        }
        //入栈
        public void push(T t)
        {
            stack.addFirst(t);
        }
        //出栈
        public T pull()
        {
            return stack.remove();
        }
        //栈是否为空
         boolean isEmpty()
         {
             return stack.isEmpty();
         }
         
         //打印栈元素
         public void display()
         {
             for(Object o:stack)
                 System.out.println(o);
         }
    }

      5、LinkedList的基本用法:

        推荐一篇介绍LinkedList基本用法的博文http://blog.csdn.net/i_lovefish/article/details/8042883,这里不再赘述。

      

  • 相关阅读:
    接口的显式实现和隐式实现
    MVC
    委托
    测试用例(TestCase)
    The remote server returned an error: NotFound.
    事件
    WCF大数据量传输配置
    多态随笔
    领域模型(domain model)
    IQueryable接口和IEnumberable接口
  • 原文地址:https://www.cnblogs.com/xujian2014/p/4630785.html
Copyright © 2011-2022 走看看