知识大全 java源码分析之LinkedList

Posted

篇首语:历览千载书,时时见遗烈。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 java源码分析之LinkedList相关的知识,希望对你有一定的参考价值。

java源码分析之LinkedList  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

    LinkedList也和ArrayList一样实现了List接口 但是它执行插入和删除操作时比ArrayList更加高效 因为它是基于链表的 基于链表也决定了它在随机访问方面要比ArrayList逊色一点       除此之外 LinkedList还提供了一些可以使其作为栈 队列 双端队列的方法 这些方法中有些彼此之间只是名称的区别 以使得这些名字在特定的上下文中显得更加的合适   先看LinkedList类的定义   public class LinkedList<E>     extends AbstractSequentialList<E>     implements List<E> Deque<E> Cloneable java io Serializable     LinkedList继承自AbstractSequenceList 实现了List及Deque接口 其实AbstractSequenceList已经实现了List接口 这里标注出List只是更加清晰而已 AbstractSequenceList提供了List接口骨干性的实现以减少实现List接口的复杂度 Deque接口定义了双端队列的操作   LinkedList中之定义了两个属性   private transient Entry<E> header = new Entry<E>(null null null); private transient int size = ;     size肯定就是LinkedList对象里面存储的元素个数了 LinkedList既然是基于链表实现的 那么这个header肯定就是链表的头结点了 Entry就是节点对象了 一下是Entry类的代码       private static class Entry<E>       E element;       Entry<E> next;       Entry<E> previous;           Entry(E element Entry<E> next Entry<E> previous)           this element = element;           this next = next;           this previous = previous;           只定义了存储的元素 前一个元素 后一个元素 这就是双向链表的节点的定义 每个节点只知道自己的前一个节点和后一个节点   来看LinkedList的构造方法     public LinkedList()     header next = header previous = header; public LinkedList(Collection<? extends E> c)     this();     addAll(c);         LinkedList提供了两个构造方法 第一个构造方法不接受参数 只是将header节点的前一节点和后一节点都设置为自身(注意 这个是一个双向循环链表 如果不是循环链表 空链表的情况应该是header节点的前一节点和后一节点均为null) 这样整个链表其实就只有header一个节点 用于表示一个空的链表 第二个构造方法接收一个Collection参数c 调用第一个构造方法构造一个空的链表 之后通过addAll将c中的元素全部添加到链表中 来看addAll的内容       public boolean addAll(Collection<? extends E> c)       return addAll(size c);     // index参数指定collection中插入的第一个元素的位置   public boolean addAll(int index Collection<? extends E> c)       // 插入位置超过了链表的长度或小于 报IndexOutOfBoundsException异常       if (index < || index > size)           throw new IndexOutOfBoundsException( Index: +index+                                                   Size: +size);     Object[] a = c toArray(); int numNew = a length; // 若需要插入的节点个数为 则返回false 表示没有插入元素     if (numNew== )         return false;     modCount++;     // 保存index处的节点 插入位置如果是size 则在头结点前面插入 否则获取index处的节点 Entry<E> successor = (index==size ? header : entry(index)); // 获取前一个节点 插入时需要修改这个节点的next引用 Entry<E> predecessor = successor previous; // 按顺序将a数组中的第一个元素插入到index处 将之后的元素插在这个元素后面     for (int i= ; i<numNew; i++) // 结合Entry的构造方法 这条语句是插入操作 相当于C语言中链表中插入节点并修改指针         Entry<E> e = new Entry<E>((E)a[i] successor predecessor);         // 插入节点后将前一节点的next指向当前节点 相当于修改前一节点的next指针         predecessor next = e;         // 相当于C语言中成功插入元素后将指针向后移动一个位置以实现循环的功能         predecessor = e; // 插入元素前index处的元素链接到插入的Collection的最后一个节点 successor previous = predecessor; // 修改size     size += numNew;     return true;       构造方法中的调用了addAll(Collection<? extends E> c)方法 而在addAll(Collection<? extends E> c)方法中仅仅是将size当做index参数调用了addAll(int index Collection<? extends E> c)方法       private Entry<E> entry(int index)           if (index < || index >= size)               throw new IndexOutOfBoundsException( Index: +index+                                                   Size: +size);           Entry<E> e = header;           // 根据这个判断决定从哪个方向遍历这个链表           if (index < (size >> ))               for (int i = ; i <= index; i++)                   e = e next;         else             // 可以通过header节点向前遍历 说明这个一个循环双向链表 header的previous指向链表的最后一个节点 这也验证了构造方法中对于header节点的前后节点均指向自己的解释             for (int i = size; i > index; i )                 e = e previous;                 return e;           结合上面代码中的注释及双向循环链表的知识 应该很容易理解LinkedList构造方法所涉及的内容 下面开始分析LinkedList的其他方法       add(E e)   public boolean add(E e)     addBefore(e header);     return true;     从上面的代码可以看出 add(E e)方法只是调用了addBefore(E e Entry<E> entry)方法 并且返回true       addBefore(E e Entry<E> entry)     private Entry<E> addBefore(E e Entry<E> entry)     Entry<E> newEntry = new Entry<E>(e entry entry previous);     newEntry previous next = newEntry;     newEntry next previous = newEntry;     size++;     modCount++;     return newEntry;       addBefore(E e Entry<E> entry)方法是个私有方法 所以无法在外部程序中调用(当然 这是一般情况 你可以通过反射上面的还是能调用到的)       addBefore(E e Entry<E> entry)先通过Entry的构造方法创建e的节点newEntry(包含了将其下一个节点设置为entry 上一个节点设置为entry previous的操作 相当于修改newEntry的 指针 ) 之后修改插入位置后newEntry的前一节点的next引用和后一节点的previous引用 使链表节点间的引用关系保持正确 之后修改和size大小和记录modCount 然后返回新插入的节点       总结 addBefore(E e Entry<E> entry)实现在entry之前插入由e构造的新节点 而add(E e)实现在header节点之前插入由e构造的新节点       add(int index E e)   public void add(int index E element)     addBefore(element (index==size ? header : entry(index)));     也是调用了addBefore(E e Entry<E> entry)方法 只是entry节点由index的值决定         构造方法 addAll(Collection<? extends E> c) add(E e) addBefor(E e Entry<E> entry)方法可以构造链表并在指定位置插入节点 为了便于理解 下面给出插入节点的示意图           addFirst(E e)   public void addFirst(E e)     addBefore(e header next);     addLast(E e)   public void addLast(E e)     addBefore(e header);     看上面的示意图 结合addBefore(E e Entry<E> entry)方法 很容易理解addFrist(E e)只需实现在header元素的下一个元素之前插入 即示意图中的一号之前 addLast(E e)只需在实现在header节点前(因为是循环链表 所以header的前一个节点就是链表的最后一个节点)插入节点(插入后在 号节点之后)       clear()       public void clear()   Entry<E> e = header next;   // e可以理解为一个移动的 指针 因为是循环链表 所以回到header的时候说明已经没有节点了   while (e != header)       // 保留e的下一个节点的引用           Entry<E> next = e next;           // 接触节点e对前后节点的引用           e next = e previous = null;           // 将节点e的内容置空         e element = null;         // 将e移动到下一个节点         e = next; // 将header构造成一个循环链表 同构造方法构造一个空的LinkedList header next = header previous = header; // 修改size     size = ;     modCount++;       上面代码中的注释已经足以解释这段代码的逻辑 需要注意的是提到的 指针 仅仅是概念上的类比 Java并不存在 指针 的概念 而只有引用 为了便于理解所以部分说明使用了 指针       contains(Object o)   public boolean contains(Object o)     return indexOf(o) != ;     仅仅只是判断o在链表中的索引 先看indexOf(Object o)方法       public int indexOf(Object o)       int index = ;       if (o==null)           for (Entry e = header next; e != header; e = e next)               if (e element==null)                   return index;               index++;                 else         for (Entry e = header next; e != header; e = e next)             if (o equals(e element))                 return index;             index++;                 return ;       indexOf(Object o)判断o链表中是否存在节点的element和o相等 若相等则返回该节点在链表中的索引位置 若不存在则放回         contains(Object o)方法通过判断indexOf(Object o)方法返回的值是否是 来判断链表中是否包含对象o       element()   public E element()     return getFirst();     getFirst()   public E getFirst()     if (size== )         throw new NoSuchElementException();     return header next element;     element()方法调用了getFirst()返回链表的第一个节点的元素 为什么要提供功能一样的两个方法 像是包装了一下名字?其实这只是为了在不同的上下文 语境 中能通过更贴切的方法名调用罢了       get(int index)   public E get(int index)     return entry(index) element;     get(int index)方法用于获得指定索引位置的节点的元素 它通过entry(int index)方法获取节点 entry(int index)方法遍历链表并获取节点 在上面有说明过 不再陈述       set(int index E element)   public E set(int index E element)     Entry<E> e = entry(index);     E oldVal = e element;     e element = element;     return oldVal;     先获取指定索引的节点 之后保留原来的元素 然后用element进行替换 之后返回原来的元素       getLast()   public E getLast()       if (size== )         throw new NoSuchElementException();     return header previous element;     getLast()方法和getFirst()方法类似 只是获取的是header节点的前一个节点的元素 因为是循环链表 所以header节点的前一节点就是链表的最后一个节点       lastIndexOf(Object o)       public int lastIndexOf(Object o)       int index = size;       if (o==null)           for (Entry e = header previous; e != header; e = e previous)               index ;               if (e element==null)                   return index;                 else         for (Entry e = header previous; e != header; e = e previous)             index ;             if (o equals(e element))                 return index;                 return ;       因为查找的是last index 即最后一次出现的位置 所以采用由后向前的遍历方式 因为采用了有后向前的遍历 所以index被赋值为size 并且循环体内执行时都进行减操作 分两种情况判断是否存在 分别是null和不为空       offer(E e)   public boolean offer(E e)     return add(e);     在链表尾部插入元素       offerFirst(E e)   public boolean offerFirst(E e)     addFirst(e);     return true;     在链表开头插入元素       offerLast(E e)   public boolean offerLast(E e)     addLast(e);     return true;     在链表末尾插入元素       上面这三个方法都只是调用了相应的add方法 同样只是提供了不同的方法名在不同的语境下使用       peek()   public E peek()     if (size== )         return null;     return getFirst();     peekFirst()   public E peekFirst()     if (size== )         return null;     return getFirst();     peekLast()   public E peekLast()     if (size== )         return null;     return getLast();     上面的三个方法也很简单 只是调用了对应的get方法       poll()   public E poll()     if (size== )         return null;     return removeFirst();     pollFirst()   public E pollFirst()     if (size== )         return null;     return removeFirst();     pollLast()   public E pollLast()     if (size== )         return null;     return removeLast();     poll相关的方法都是获取并移除某个元素 都是和remove操作相关       pop()   public E pop()     return removeFirst();     push(E e)   public void push(E e)     addFirst(e);     这两个方法对应栈的操作 即弹出一个元素和压入一个元素 仅仅是调用了removeFirst()和addFirst()方法       下面集中看remove相关操作的方法       remove()   public E remove()     return removeFirst();     remove(int index)   public E remove(int index)     return remove(entry(index));     remove(Object o)       public boolean remove(Object o)       if (o==null)           for (Entry<E> e = header next; e != header; e = e next)               if (e element==null)                   remove(e);                   return true;                               else         for (Entry<E> e = header next; e != header; e = e next)             if (o equals(e element))                 remove(e);                 return true;                             return false;       removeFirst()   public E removeFirst()     return remove(header next);     removeLast()   public E removeLast()     return remove(header previous);     removeFirstOccurrence()   public boolean removeFirstOccurrence(Object o)     return remove(o);     removeLastOccurence()       public boolean removeLastOccurrence(Object o)       if (o==null)           for (Entry<E> e = header previous; e != header; e = e previous)               if (e element==null)                   remove(e);                   return true;                               else         for (Entry<E> e = header previous; e != header; e = e previous)             if (o equals(e element))                 remove(e);                 return true;                             return false;       几个remove方法最终都是调用了一个私有方法 remove(Entry<E> e) 只是其他简单逻辑上的区别 下面分析remove(Entry<E> e)方法       private E remove(Entry<E> e)       if (e == header)           throw new NoSuchElementException();       // 保留将被移除的节点e的内容   E result = e element;   // 将前一节点的next引用赋值为e的下一节点       e previous next = e next;       // 将e的下一节点的previous赋值为e的上一节点       e next previous = e previous;     // 上面两条语句的执行已经导致了无法在链表中访问到e节点 而下面解除了e节点对前后节点的引用 e next = e previous = null; // 将被移除的节点的内容设为null e element = null; // 修改size大小     size ;     modCount++;     // 返回移除节点e的内容     return result;       clone()       public Object clone()       LinkedList<E> clone = null;       try           clone = (LinkedList<E>) super clone();       catch (CloneNotSupportedException e)           throw new InternalError();             clone header = new Entry<E>(null null null);       clone header next = clone header previous = clone header;     clone size = ;     clone modCount = ;     for (Entry<E> e = header next; e != header; e = e next)         clone add(e element);     return clone;       调用父类的clone()方法初始化对象链表clone 将clone构造成一个空的双向循环链表 之后将header的下一个节点开始将逐个节点添加到clone中 最后返回克隆的clone对象       toArray()     public Object[] toArray()     Object[] result = new Object[size];     int i = ;     for (Entry<E> e = header next; e != header; e = e next)         result[i++] = e element;     return result;       创建大小和LinkedList相等的数组result 遍历链表 将每个节点的元素element复制到数组中 返回数组       toArray(T[] a)       public <T> T[] toArray(T[] a)       if (a length < size)           a = (T[])java lang reflect Array newInstance(                                   a getClass() getComponentType() size);       int i = ;       Object[] result = a;       for (Entry<E> e = header next; e != header; e = e next)           result[i++] = e element;       if (a length > size)         a[size] = null;     return a;       先判断出入的数组a的大小是否足够 若大小不够则拓展 这里用到了发射的方法 重新实例化了一个大小为size的数组 之后将数组a赋值给数组result 遍历链表向result中添加的元素 最后判断数组a的长度是否大于size 若大于则将size位置的内容设置为null 返回a       从代码中可以看出 数组a的length小于等于size时 a中所有元素被覆蓋 被拓展来的空间存储的内容都是null 若数组a的length的length大于size 则 至size 位置的内容被覆蓋 size位置的元素被设置为null size之后的元素不变       为什么不直接对数组a进行操作 要将a赋值给result数组之后对result数组进行操作?           LinkedList的Iterator       除了Entry LinkedList还有一个内部类 ListItr       ListItr实现了ListIterator接口 可知它是一个迭代器 通过它可以遍历修改LinkedList       在LinkedList中提供了获取ListItr对象的方法 listIterator(int index)   public ListIterator<E> listIterator(int index)     return new ListItr(index);     该方法只是简单的返回了一个ListItr对象       LinkedList中还有通过集成获得的listIterator()方法 该方法只是调用了listIterator(int index)并且传入       下面详细分析ListItr       private class ListItr implements ListIterator<E>   // 最近一次返回的节点 也是当前持有的节点       private Entry<E> lastReturned = header;       // 对下一个元素的引用       private Entry<E> next;       // 下一个节点的index       private int nextIndex;       private int expectedModCount = modCount;       // 构造方法 接收一个index参数 返回一个ListItr对象       ListItr(int index)           // 如果index小于 或大于size 抛出IndexOutOfBoundsException异常           if (index < || index > size)           throw new IndexOutOfBoundsException( Index: +index+                               Size: +size);           // 判断遍历方向           if (index < (size >> ))           // next赋值为第一个节点           next = header next;           // 获取指定位置的节点           for (nextIndex= ; nextIndex<index; nextIndex++)               next = next next;           else   // else中的处理和if块中的处理一致 只是遍历方向不同           next = header;           for (nextIndex=size; nextIndex>index; nextIndex )               next = next previous;                       // 根据nextIndex是否等于size判断时候还有下一个节点(也可以理解为是否遍历完了LinkedList)       public boolean hasNext()           return nextIndex != size;             // 获取下一个元素       public E next()           checkForComodification();           // 如果nextIndex==size 则已经遍历完链表 即没有下一个节点了(实际上是有的 因为是循环链表 任何一个节点都会有上一个和下一个节点 这里的没有下一个节点只是说所有节点都已经遍历完了)           if (nextIndex == size)           throw new NoSuchElementException();           // 设置最近一次返回的节点为next节点           lastReturned = next;           // 将next 向后移动一位           next = next next;           // index计数加           nextIndex++;           // 返回lastReturned的元素           return lastReturned element;                 public boolean hasPrevious()           return nextIndex != ;             // 返回上一个节点 和next()方法相似       public E previous()           if (nextIndex == )           throw new NoSuchElementException();               lastReturned = next = next previous;           nextIndex ;           checkForComodification();           return lastReturned element;                 public int nextIndex()           return nextIndex;                 public int previousIndex()           return nextIndex ;             // 移除当前Iterator持有的节点       public void remove()               checkForComodification();               Entry<E> lastNext = lastReturned next;               try                   LinkedList this remove(lastReturned);               catch (NoSuchElementException e)                   throw new IllegalStateException();                         if (next==lastReturned)                   next = lastNext;               else           nextIndex ;           lastReturned = header;           expectedModCount++;             // 修改当前节点的内容       public void set(E e)           if (lastReturned == header)           throw new IllegalStateException();           checkForComodification();           lastReturned element = e;             // 在当前持有节点后面插入新节点       public void add(E e)           checkForComodification();           // 将最近一次返回节点修改为header           lastReturned = header;           addBefore(e next);           nextIndex++;         expectedModCount++;         // 判断expectedModCount和modCount是否一致 以确保通过ListItr的修改操作正确的反映在LinkedList中     final void checkForComodification()         if (modCount != expectedModCount)         throw new ConcurrentModificationException();           下面是一个ListItr的使用实例       LinkedList<String> list = new LinkedList<String>();           list add( First );           list add( Second );           list add( Thrid );           System out println(list);           ListIterator<String> itr = list listIterator();           while (itr hasNext())               System out println(itr next());                   try             System out println(itr next());// throw Exception         catch (Exception e)             // TODO: handle exception                 itr = list listIterator();         System out println(list);         System out println(itr next());         itr add( new node );         System out println(list);         itr add( new node );         System out println(list);         System out println(itr next());         itr set( modify node );         System out println(list);         itr remove();         System out println(list);       结果   [First Second Thrid]   First   Second   Thrid   [First Second Thrid]   First   [First new node Second Thrid]   [First new node new node Second Thrid] Second [First new node new node modify node Thrid] [First new node new node Thrid]       LinkedList还有一个提供Iterator的方法 descendingIterator() 该方法返回一个DescendingIterator对象 DescendingIterator是LinkedList的一个内部类   public Iterator<E> descendingIterator()     return new DescendingIterator();     下面分析详细分析DescendingIterator类       private class DescendingIterator implements Iterator       // 获取ListItr对象   final ListItr itr = new ListItr(size());   // hasNext其实是调用了itr的hasPrevious方法       public boolean hasNext()           return itr hasPrevious();         // next()其实是调用了itr的previous方法       public E next()         return itr previous();         public void remove()         itr remove();           从类名和上面的代码可以看出这是一个反向的Iterator 代码很简单 都是调用的ListItr类中的方法   cha138/Article/program/Java/hx/201311/26953

相关参考

知识大全 java源码分析之LinkedHashMap

java源码分析之LinkedHashMap  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nbs

知识大全 enoeht的Java源码系列之处理配置文件

enoeht的Java源码系列之处理配置文件  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  我们

知识大全 Java中ArrayList和LinkedList区别

Java中ArrayList和LinkedList区别  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧

知识大全 Java中SOCKET通讯源码

Java中SOCKET通讯源码  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  //ServeOn

知识大全 java.util.HashMap源码要点浅析

java.util.HashMap源码要点浅析  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  散

知识大全 源码实现实时获取Java堆内存信息

源码实现实时获取Java堆内存信息  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  如果大家有遇到

知识大全 Java兼容性测试,开放源码之路

Jacks:Java兼容性测试,开放源码之路  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!什么是J

知识大全 Java实现多种幻灯片切换特效(附源码)

Java实现多种幻灯片切换特效(附源码)  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  功能说明

知识大全 如何有效防止Java程序源码被人偷窥

如何有效防止Java程序源码被人偷窥?  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &

知识大全 java阻塞队列 线程同步合作

  Queue接口与ListSet同一级别都是继承了Collection接口LinkedList实现了Queue接口Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果