自己动手写写:LinkedList源码浅析

http://boy00fly.iteye.com/blog/1138904

上篇文章浅析了ArrayList的源码相关内容!这篇文章将介绍LinkedList相关的内容!

 

二. LinkedList

 

先来看看LinkedList的类结构!

Java代码   收藏代码
  1. public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable  

 

 

1. 几个重要的成员变量

Java代码   收藏代码
  1. private transient Entry<E> header = new Entry<E>(nullnullnull);  
  2.       
  3. private transient int size = 0;//LinkedList的长度<span style="white-space: normal;"> ,初始化为0</span>  

这里先来说一下header这个变量,这个很重要哦!

 

首先看一下Entry是个什么东西!

Java代码   收藏代码
  1. private static class Entry<E>  
  2. {  
  3.         E element;  
  4.           
  5.         Entry<E> next;  
  6.           
  7.         Entry<E> previous;  
  8.           
  9.         Entry(E element, Entry<E> next, Entry<E> previous)  
  10.         {  
  11.             this.element = element;  
  12.             this.next = next;  
  13.             this.previous = previous;  
  14.         }  
  15. }  

对的,Entry就是LinkedList的一个内部静态类!我们知道LinkedList的内部数据结构采用的链式存储方式,链式存储决定了它插入的速度会相对会快,而索引的速度慢!链式存储最主要的有三个元素:当前元素、前一个元素地址、后一个元素地址

Entry类中element表示当前元素; next表示后一个元素;previous表示前一个元素;这样的话一个链式的存储的模型就有了。

 

2. 两个构造函数

Java代码   收藏代码
  1.  /** 
  2.    * Constructs an empty list. 
  3.    */  
  4. public LinkedList()  
  5. {  
  6.       header.next = header.previous = header;  
  7. }  

无参构造函数初始化的时候header的前一个、后一个均指向本身

 

 

Java代码   收藏代码
  1. /** 
  2.  * Constructs a list containing the elements of the specified 
  3.  * collection, in the order they are returned by the collection's 
  4.  * iterator. 
  5.  * 
  6.  * @param  c the collection whose elements are to be placed into this list 
  7.    * @throws NullPointerException if the specified collection is null 
  8.    */  
  9.   public LinkedList(Collection<? extends E> c)  
  10.   {  
  11.       this();  
  12.       addAll(c);  
  13.   }  

根据已有的Collection初始化LinkedList,我们来看一个addAll(int index, Collection<? extends E> c)这个方法(addAll(Collection<? extends E> c)本身也是调用的此方法,其中index的参数值为size而已)。

 

Java代码   收藏代码
  1. /**                                                                                  
  2.  * Inserts all of the elements in the specified collection into this                 
  3.  * list, starting at the specified position.  Shifts the element                     
  4.  * currently at that position (if any) and any subsequent elements to                
  5.  * the right (increases their indices).  The new elements will appear                
  6.  * in the list in the order that they are returned by the                            
  7.  * specified collection's iterator.                                                  
  8.  *                                                                                   
  9.  * @param index index at which to insert the first element                           
  10.  *              from the specified collection                                        
  11.  * @param c collection containing elements to be added to this list                  
  12.  * @return <tt>true</tt> if this list changed as a result of the call                
  13.  * @throws IndexOutOfBoundsException {@inheritDoc}                                   
  14.  * @throws NullPointerException if the specified collection is null                  
  15.  */                                                                                   
  16. public boolean addAll(int index, Collection<? extends E> c)                           
  17. {                                                                                     
  18.     if (index < 0 || index > size)                                                    
  19.         throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);   
  20.     Object[] a = c.toArray();                                                         
  21.     int numNew = a.length;                                                            
  22.     if (numNew == 0)                                                                  
  23.         return false;                                                                 
  24.     modCount++;                                                                       
  25.                                                                                       
  26.     Entry<E> successor = (index == size ? header : entry(index));                     
  27.     Entry<E> predecessor = successor.previous;                                        
  28.     for (int i = 0; i < numNew; i++)                                                  
  29.     {                                                                                 
  30.         Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);                   
  31.         predecessor.next = e;                                                         
  32.         predecessor = e;                                                              
  33.     }                                                                                 
  34.     successor.previous = predecessor;                                                 
  35.                                                                                       
  36.     size += numNew;                                                                   
  37.     return true;                                                                      
  38. }                                                                                     

前面的我就不说了,都是一些预判断。我们来分析下下面这段代码的含义

Java代码   收藏代码
  1. Entry<E> successor = (index == size ? header : entry(index));                     
  2. Entry<E> predecessor = successor.previous;  

 

粗略的看一个successorpredecessor 英文含义分别表示为后继者前辈;对呀,我们想啊,LinkedList本身是一个链式存储结构,你要将一些内容插入进来,首先必须要在链上找到一个入口,然后将此入口掐断,接着将你插入进来的内容放进去,最后再将这个链给接起来,你要将链给接起来要接对啊,不能接错地方啊!successorpredecessor就是为了将链接对所用,分别指向链断裂后,后一段和前一段的链接地址

我们来看下面这两张图:


图一


图二
上面我描述的过程就类似于从图一到图二的过程。

 

初步感性的分析玩之后我们来详细分析一下!

这里做了一个判断:如果当前插入的index的值等于size则返回header,否则返回entry(index);

1. index == size时,其实就是插入到链尾处,因为链尾处的元素的next比较特殊,它指向了链首header

2. index != size时,我们先来看一下entry(int index)这个方法的源码:

Java代码   收藏代码
  1. /**                                                                                 
  2.  * Returns the indexed entry.                                                       
  3.  */                                                                                  
  4. private Entry<E> entry(int index)                                                    
  5. {                                                                                    
  6.     if (index < 0 || index >= size)                                                  
  7.         throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);  
  8.     Entry<E> e = header;                                                             
  9.     if (index < (size >> 1))                                                         
  10.     {                                                                                
  11.         for (int i = 0; i <= index; i++)                                             
  12.             e = e.next;                                                              
  13.     }                                                                                
  14.     else                                                                             
  15.     {                                                                                
  16.         for (int i = size; i > index; i--)                                           
  17.             e = e.previous;                                                          
  18.     }                                                                                
  19.     return e;                                                                        
  20. }                                                                                     

其实就是找到找到索引值为index的Entry,判断index值大小是否已经超过了size值的一半,如果没超过就从链表头部开始遍历,否则从链表尾部开始遍历。

ps:看得出来,按索引找个Entry这么麻烦,真慢,不像ArrayList来得那么直接。这就是为什么LinkedList索引慢的原因,存储结构决定的,基因问题,呵呵。

 

3. Entry<E> predecessor = successor.previous; 鉴于上面的分析,这句也就不难理解了。

4.

Java代码   收藏代码
  1. for (int i = 0; i < numNew; i++)                                                  
  2. {                                                                                 
  3.         Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);                   
  4.         predecessor.next = e;                                                         
  5.         predecessor = e;                                                              
  6. }  

 

 这段代码就是将初始化的Collection数据一个一个链接上去。

 

Java代码   收藏代码
  1. successor.previous = predecessor;   
  2. size += numNew;  

 最后将这个链给接回去,形成了一个闭环链! LinkedList的大小也要增加一下嘛!

 

   

3. 几个常用方法浅析

Java代码   收藏代码
  1. /**                                                                      
  2.  * Inserts the specified element at the specified position in this list. 
  3.  * Shifts the element currently at that position (if any) and any        
  4.  * subsequent elements to the right (adds one to their indices).         
  5.  *                                                                       
  6.  * @param index index at which the specified element is to be inserted   
  7.  * @param element element to be inserted                                 
  8.  * @throws IndexOutOfBoundsException {@inheritDoc}                       
  9.  */                                                                       
  10. public void add(int index, E element)                                     
  11. {                                                                         
  12.     addBefore(element, (index == size ? header : entry(index)));          
  13. }                                                                         

 

 有些代码是不是很熟悉啊?呵呵,还是看内部的addBefore(E e, Entry<E> entry)方法吧。

Java代码   收藏代码
  1.  private Entry<E> addBefore(E e, Entry<E> entry)  
  2.  {  
  3.         Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);  
  4.         newEntry.previous.next = newEntry;  
  5.         newEntry.next.previous = newEntry;  
  6.         size++;  
  7.         modCount++;  
  8.         return newEntry;  
  9. }  

 这个还用我说吗? 是不是比在构造方法里面的要简单得多啊。

 

 

Java代码   收藏代码
  1. /**                                                            
  2.  * Returns the element at the specified position in this list. 
  3.  *                                                             
  4.  * @param index index of the element to return                 
  5.  * @return the element at the specified position in this list  
  6.  * @throws IndexOutOfBoundsException {@inheritDoc}             
  7.  */                                                             
  8. public E get(int index)                                         
  9. {                                                               
  10.     return entry(index).element;                                
  11. }                                                               

   获取指定索引值index的元素,呵呵,不解释了!

 

 

Java代码   收藏代码
  1. /**                                                     
  2.  * Returns the first element in this list.              
  3.  *                                                      
  4.  * @return the first element in this list               
  5.  * @throws NoSuchElementException if this list is empty 
  6.  */                                                      
  7. public E getFirst()                                      
  8. {                                                        
  9.     if (size == 0)                                       
  10.         throw new NoSuchElementException();              
  11.                                                          
  12.     return header.next.element;                          
  13. }                                                        
  14.   
  15. /**                                                     
  16.  * Returns the last element in this list.               
  17.  *                                                      
  18.  * @return the last element in this list                
  19.  * @throws NoSuchElementException if this list is empty 
  20.  */                                                      
  21. public E getLast()                                       
  22. {                                                        
  23.     if (size == 0)                                       
  24.         throw new NoSuchElementException();              
  25.                                                          
  26.     return header.previous.element;                      
  27. }                                                        

 分别为获取第一个和最后一个元素的值,也很简单啊,第一个元素就是header的next的element的值,最后一个元素就是header的previous的element的值。

ps:真实的内部情况是这样的,整个链表的内容是包含header的Entry和LinkedList存储的所有Entry两个部分共同组成的。

 

 

Java代码   收藏代码
  1. /**                                                                            
  2.  * Returns the index of the first occurrence of the specified element          
  3.  * in this list, or -1 if this list does not contain the element.              
  4.  * More formally, returns the lowest index <tt>i</tt> such that                
  5.  * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,   
  6.  * or -1 if there is no such index.                                            
  7.  *                                                                             
  8.  * @param o element to search for                                              
  9.  * @return the index of the first occurrence of the specified element in       
  10.  *         this list, or -1 if this list does not contain the element          
  11.  */                                                                             
  12. public int indexOf(Object o)                                                    
  13. {                                                                               
  14.     int index = 0;                                                              
  15.     if (o == null)                                                              
  16.     {                                                                           
  17.         for (Entry e = header.next; e != header; e = e.next)                    
  18.         {                                                                       
  19.             if (e.element == null)                                              
  20.                 return index;                                                   
  21.             index++;                                                            
  22.         }                                                                       
  23.     }                                                                           
  24.     else                                                                        
  25.     {                                                                           
  26.         for (Entry e = header.next; e != header; e = e.next)                    
  27.         {                                                                       
  28.             if (o.equals(e.element))                                            
  29.                 return index;                                                   
  30.             index++;                                                            
  31.         }                                                                       
  32.     }                                                                           
  33.     return -1;                                                                  
  34. }                                                                               

返回第一个包含对象o的索引值。也很简单吧,对吧!

 

 

 

Java代码   收藏代码
  1. private E remove(Entry<E> e)                 
  2. {                                            
  3.     if (e == header)                         
  4.         throw new NoSuchElementException();  
  5.                                              
  6.     E result = e.element;                    
  7.     e.previous.next = e.next;                
  8.     e.next.previous = e.previous;            
  9.     e.next = e.previous = null;              
  10.     e.element = null;                        
  11.     size--;                                  
  12.     modCount++;                              
  13.     return result;                           
  14. }                                            

这是一个private方法,就是删除链表中的一个Entry,思路如下:将要移除的对象e的previous的next指向e的next,对象e的next的previous指向e的previous,就是将链表的一个节点删掉,在将这个链表接起来。

 

 

 

Java代码   收藏代码
  1. /**                                                                     
  2.  * Replaces the element at the specified position in this list with the 
  3.  * specified element.                                                   
  4.  *                                                                      
  5.  * @param index index of the element to replace                         
  6.  * @param element element to be stored at the specified position        
  7.  * @return the element previously at the specified position             
  8.  * @throws IndexOutOfBoundsException {@inheritDoc}                      
  9.  */                                                                      
  10. public E set(int index, E element)                                       
  11. {                                                                        
  12.     Entry<E> e = entry(index);                                           
  13.     E oldVal = e.element;                                                
  14.     e.element = element;                                                 
  15.     return oldVal;                                                       
  16. }                                                                        

 这个更简单,就是替换索引index的Entry的element的值。

 

好吧,基本常用的方法也都分析完了,重点已经照顾到了,其他的就不再累述了。

 

这里就简单描述一下Vector吧!

 

Java代码   收藏代码
  1. public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable  

 Vector用得并不多,它和ArrayList很相似,但是它本身是线程安全的,看源码就能够看得出来,很多方法都是synchronized,但是在jdk1.6上就已经有java.util.concurrent了,对于多线程编程的话,Vector的用武之地也很少了,这里就不再讲了!

 

 

ps:附件中我上传了一个比较不错的Data Structure Visualzation工具,是一个jar包运行java -jar 执行。对于分析与理解数据结构方面的问题相当有帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值