`
boy00fly
  • 浏览: 197716 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

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

 
阅读更多

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

 

二. LinkedList

 

先来看看LinkedList的类结构!

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable

 

 

1. 几个重要的成员变量

private transient Entry<E> header = new Entry<E>(null, null, null);
    
private transient int size = 0;//LinkedList的长度 ,初始化为0

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

 

首先看一下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;
        }
}

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

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

 

2. 两个构造函数

 /**
   * Constructs an empty list.
   */
public LinkedList()
{
      header.next = header.previous = header;
}

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

 

 

  /**
   * Constructs a list containing the elements of the specified
   * collection, in the order they are returned by the collection's
   * iterator.
   *
   * @param  c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public LinkedList(Collection<? extends E> c)
    {
        this();
        addAll(c);
    }

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

 

/**                                                                                 
 * Inserts all of the elements in the specified collection into this                
 * list, starting at the specified position.  Shifts the element                    
 * currently at that position (if any) and any subsequent elements to               
 * the right (increases their indices).  The new elements will appear               
 * in the list in the order that they are returned by the                           
 * specified collection's iterator.                                                 
 *                                                                                  
 * @param index index at which to insert the first element                          
 *              from the specified collection                                       
 * @param c collection containing elements to be added to this list                 
 * @return <tt>true</tt> if this list changed as a result of the call               
 * @throws IndexOutOfBoundsException {@inheritDoc}                                  
 * @throws NullPointerException if the specified collection is null                 
 */                                                                                 
public boolean addAll(int index, Collection<? extends E> c)                         
{                                                                                   
    if (index < 0 || index > size)                                                  
        throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); 
    Object[] a = c.toArray();                                                       
    int numNew = a.length;                                                          
    if (numNew == 0)                                                                
        return false;                                                               
    modCount++;                                                                     
                                                                                    
    Entry<E> successor = (index == size ? header : entry(index));                   
    Entry<E> predecessor = successor.previous;                                      
    for (int i = 0; i < numNew; i++)                                                
    {                                                                               
        Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);                 
        predecessor.next = e;                                                       
        predecessor = e;                                                            
    }                                                                               
    successor.previous = predecessor;                                               
                                                                                    
    size += numNew;                                                                 
    return true;                                                                    
}                                                                                   

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

 Entry<E> successor = (index == size ? header : entry(index));                   
 Entry<E> predecessor = successor.previous;

 

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

我们来看下面这两张图:


图一


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

 

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

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

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

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

/**                                                                                
 * Returns the indexed entry.                                                      
 */                                                                                
private Entry<E> entry(int index)                                                  
{                                                                                  
    if (index < 0 || index >= size)                                                
        throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
    Entry<E> e = header;                                                           
    if (index < (size >> 1))                                                       
    {                                                                              
        for (int i = 0; i <= index; i++)                                           
            e = e.next;                                                            
    }                                                                              
    else                                                                           
    {                                                                              
        for (int i = size; i > index; i--)                                         
            e = e.previous;                                                        
    }                                                                              
    return e;                                                                      
}                                                                                   

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

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

 

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

4.

for (int i = 0; i < numNew; i++)                                                
{                                                                               
        Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);                 
        predecessor.next = e;                                                       
        predecessor = e;                                                            
}

 

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

 

successor.previous = predecessor; 
size += numNew;

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

 

   

3. 几个常用方法浅析

/**                                                                     
 * Inserts the specified element at the specified position in this list.
 * Shifts the element currently at that position (if any) and any       
 * subsequent elements to the right (adds one to their indices).        
 *                                                                      
 * @param index index at which the specified element is to be inserted  
 * @param element element to be inserted                                
 * @throws IndexOutOfBoundsException {@inheritDoc}                      
 */                                                                     
public void add(int index, E element)                                   
{                                                                       
    addBefore(element, (index == size ? header : entry(index)));        
}                                                                       

 

 有些代码是不是很熟悉啊?呵呵,还是看内部的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;
}

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

 

 

/**                                                           
 * Returns the element at the specified position in this list.
 *                                                            
 * @param index index of the element to return                
 * @return the element at the specified position in this list 
 * @throws IndexOutOfBoundsException {@inheritDoc}            
 */                                                           
public E get(int index)                                       
{                                                             
    return entry(index).element;                              
}                                                             

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

 

 

/**                                                    
 * Returns the first element in this list.             
 *                                                     
 * @return the first element in this list              
 * @throws NoSuchElementException if this list is empty
 */                                                    
public E getFirst()                                    
{                                                      
    if (size == 0)                                     
        throw new NoSuchElementException();            
                                                       
    return header.next.element;                        
}                                                      

/**                                                    
 * Returns the last element in this list.              
 *                                                     
 * @return the last element in this list               
 * @throws NoSuchElementException if this list is empty
 */                                                    
public E getLast()                                     
{                                                      
    if (size == 0)                                     
        throw new NoSuchElementException();            
                                                       
    return header.previous.element;                    
}                                                      

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

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

 

 

/**                                                                           
 * Returns the index of the first occurrence of the specified element         
 * in this list, or -1 if this list does not contain the element.             
 * More formally, returns the lowest index <tt>i</tt> such that               
 * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,  
 * or -1 if there is no such index.                                           
 *                                                                            
 * @param o element to search for                                             
 * @return the index of the first occurrence of the specified element in      
 *         this list, or -1 if this list does not contain the element         
 */                                                                           
public int indexOf(Object o)                                                  
{                                                                             
    int index = 0;                                                            
    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 -1;                                                                
}                                                                             

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

 

 

 

private E remove(Entry<E> e)               
{                                          
    if (e == header)                       
        throw new NoSuchElementException();
                                           
    E result = e.element;                  
    e.previous.next = e.next;              
    e.next.previous = e.previous;          
    e.next = e.previous = null;            
    e.element = null;                      
    size--;                                
    modCount++;                            
    return result;                         
}                                          

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

 

 

 

/**                                                                    
 * Replaces the element at the specified position in this list with the
 * specified element.                                                  
 *                                                                     
 * @param index index of the element to replace                        
 * @param element element to be stored at the specified position       
 * @return the element previously at the specified position            
 * @throws IndexOutOfBoundsException {@inheritDoc}                     
 */                                                                    
public E set(int index, E element)                                     
{                                                                      
    Entry<E> e = entry(index);                                         
    E oldVal = e.element;                                              
    e.element = element;                                               
    return oldVal;                                                     
}                                                                      

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

 

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

 

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

 

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 执行。对于分析与理解数据结构方面的问题相当有帮助。

  • 大小: 6.4 KB
  • 大小: 4.6 KB
1
2
分享到:
评论

相关推荐

    源码解析jdk7.0集合:LinkedList的底层实现原理.pdf

    接下来,我们将通过JDK 7.0的源码,深入探讨LinkedList的底层实现原理。 首先,LinkedList实现了List接口,因此它是一个有序列表。它还实现了Deque接口,这意味着LinkedList支持双端队列的操作。除了这些接口之外,...

    java软件技术文档-深入java8的集合2:LinkedList的实现原理.pdf

    《深入Java8的集合2:LinkedList的实现原理》 LinkedList是Java编程语言中实现List接口的一种数据结构,它采用双链表的方式存储元素。本文将深入解析LinkedList的内部机制,帮助开发者理解其工作原理。 首先,...

    Jdk1.6 Collections Framework源码解析(2)-LinkedList

    《Jdk1.6 Collections Framework源码解析(2)-LinkedList》 LinkedList是Java集合框架中的一个重要的类,它是List接口的实现,同时继承了AbstractSequentialList,并实现了Deque接口。LinkedList是一种双链表结构,...

    LinkedList 部分源码

    ### LinkedList部分源码解析 #### 一、简介 `LinkedList`是Java集合框架的一个重要组成部分,它基于双向链表实现,既支持`List`接口也实现了`Deque`接口,因此可以作为列表、栈或者队列使用。双向链表的每个节点...

    尚硅谷-深入java8的集合2:LinkedList的实现原理.pdf

    ·企业级笔试面试题目深入源码级讲解,拒绝死记硬背 4.代码量更大、案例更丰富、更贴近实战: ·Java语言基础阶段:12720行代码,Java语言高级阶段:11684行代码 ·课堂实战项目3套,课后实战项目2套 ·近百道企业...

    LinkedList源码

    LinkedList源码

    JDK1.6中Arraylist,Vector,LinkedList源码

    在Java编程语言中,ArrayList、Vector和LinkedList是三种常见的动态数组实现,它们都属于集合框架中的List接口。这里我们将深入探讨这三种数据结构的源码,理解它们的内部实现、性能特性和适用场景。 首先,...

    LinkedList:LinkedList上的程序

    LinkedList是Java中的一个类,位于java.util包下,它是List接口的一个实现,主要用于存储动态的、有序的数据集合。与数组不同,链表的元素不需要在内存中连续存储,这使得在链表中插入和删除元素具有较高的效率。 ...

    LinkedList.zip

    《深入理解Java中的LinkedList》 在Java编程语言中,LinkedList是一种重要的数据结构,它属于集合框架的一部分,位于java.util包下。LinkedList是一个双向链表,它不仅提供了存储元素的功能,还支持快速地在链表的...

    LinkedList源码学习分析

    《LinkedList源码学习分析》 LinkedList作为Java集合框架中的一员,是基于链表数据结构实现的线程不安全容器。本文将深入探讨LinkedList的实现原理、核心方法的代码实现,并对比ArrayList,理解其特性和使用场景。 ...

    第三章 LinkedList源码解析1

    LinkedList源码解析 LinkedList是Java中的一种链表实现,它的底层是一个环形双向链表。在 LinkedList 中,节点之间通过引用相互连接,形成一个链表结构。LinkedList 提供了多种方法来操作链表,包括添加、删除、...

    LinkedList_example:LinkedList Javascript

    在JavaScript编程中,LinkedList是一种常见的数据结构,它与数组不同,具有特定的性能优势,尤其在插入和删除操作上。本篇文章将深入探讨JavaScript中的LinkedList及其应用。 LinkedList是由一系列节点构成的链式...

    LinkedList源码分析_016.pdf

    《LinkedList源码解析》 LinkedList是Java集合框架中的一员,它是AbstractSequentialList的子类,同时也实现了List、Deque和Cloneable、Serializable接口。LinkedList作为双向链表,支持快速的添加、删除元素,尤其...

    Java集合框架源码剖析:LinkedList

    Java集合框架中的LinkedList是一个强大的数据结构,因为它同时实现了List接口和Deque接口,使其既具备顺序容器的功能,又能作为队列和栈使用。LinkedList基于双向链表实现,这意味着它支持高效地在两端添加和删除...

    linkedList:LinkedlIst上的操作

    `linkedList`标题暗示我们将探讨的是与链表相关的操作,而描述中提到的“在LinkedlIst上的操作”进一步确认了这一点。在这个C语言实现的链表项目中,`linkedList-master`可能是源代码文件夹,包含了链表操作的相关...

    比较ArrayList、LinkedList、Vector1

    【ArrayList、LinkedList、Vector对比分析】 1. **List概述** List接口是Java集合框架中的重要组成部分,它是一个有序的集合,允许重复元素,并且保持插入顺序。List接口的实现类主要有ArrayList、LinkedList和...

    ArrayList-LinkedList-源码.rar

    《ArrayList与LinkedList源码解析》 在Java编程中,ArrayList和LinkedList是两种常见的动态数组,它们都是Java集合框架的一部分,提供了对元素存储和操作的功能。本篇将深入探讨ArrayList和LinkedList的内部实现...

    JAVA1234:J2SE源码PART1

    第十五讲和第十六讲可能涵盖Java的集合框架,包括ArrayList、LinkedList、HashSet、HashMap等数据结构的源码分析。集合框架是Java库的重要组成部分,提供了存储和操作对象的高效工具。通过对这些类的源码学习,我们...

    LinkedList源码分析

    ### LinkedList源码分析 #### 一、概述 `LinkedList` 是 Java 集合框架中的一个重要组成部分,它基于双向链表实现。与基于数组的 `ArrayList` 相比,`LinkedList` 在插入和删除操作上更为高效,因为它只需要改变...

    LinkedList实现List一些方法

    LinkedList是Java集合框架中的一种数据结构,主要用于存储有序的元素序列。它实现了List接口,这意味着它支持添加、删除、修改和遍历元素等操作。在本文中,我们将深入探讨LinkedList如何实现List接口的一些主要方法...

Global site tag (gtag.js) - Google Analytics