LinkedList源码解析

首先放出类图

然后是成员变量和构造方法

 private transient Entry<E> header = new Entry(null, null, null);
  private transient int size = 0;
  private static final long serialVersionUID = 876323262645176354L;
  
  public LinkedList()
  {
    this.header.next = (this.header.previous = this.header);
  }
  
  public LinkedList(Collection<? extends E> paramCollection)
  {
    this();
    addAll(paramCollection);
  }

在无参构造方法里面用到了一个对象 Entry(节点类)

  private static class Entry<E>
  {
    E element;
    Entry<E> next;
    Entry<E> previous;
    
    Entry(E paramE, Entry<E> paramEntry1, Entry<E> paramEntry2)
    {
      this.element = paramE;
      this.next = paramEntry1;
      this.previous = paramEntry2;
    }
  }
header是双向链表的头节点,它是双向链表节点所对应的类Entry的实例。Entry中包含成员变量: previous, next, element。其中,previous是该节点的上一个节点,next是该节点的下一个节点,element是该节点所包含的值。 
  size是双向链表中节点实例的个数

注意这一段

 this.header.next = (this.header.previous = this.header);
第一个构造方法不接受参数,将header实例的previous和next全部指向header实例(注意,这个是一个双向循环链表,如果不是循环链表,空链表的情况应该是header节点的前一节点和后一节点均为null),这样整个链表其实就只有header一个节点,用于表示一个空的链表。

其实

LinkedList底层的数据结构是基于双向循环链表的,且头结点中不存放数据,如下:

 

既然是双向链表,那么必定存在一种数据结构——我们可以称之为节点,节点实例保存业务数据,前一个节点的位置信息和后一个节点位置信息,如下图所示:



看一下add方法

 public boolean add(E paramE)
  {
    addBefore(paramE, this.header);
    return true;
  }
addBefore

 private Entry<E> addBefore(E paramE, Entry<E> paramEntry)
  {
    Entry localEntry = new Entry(paramE, paramEntry, paramEntry.previous);
    localEntry.previous.next = localEntry;
    localEntry.next.previous = localEntry;
    this.size += 1;
    this.modCount += 1;
    return localEntry;
  }

下面分解“添加第一个数据”的步骤:

第一步:初始化后LinkedList实例的情况:

第二步:初始化一个预添加的Entry实例(newEntry)。

Entry newEntry = newEntry(e, entry, entry.previous);

 

 

第三步:调整新加入节点和头结点(header)的前后指针。

newEntry.previous.next = newEntry;

newEntry.previous即header,newEntry.previous.next即header的next指向newEntry实例。在上图中应该是“4号线”指向newEntry。

newEntry.next.previous = newEntry;

newEntry.next即header,newEntry.next.previous即header的previous指向newEntry实例。在上图中应该是“3号线”指向newEntry。

调整后如下图所示:

图——加入第一个节点后LinkedList示意图

下面分解“添加第二个数据”的步骤:

第一步:新建节点。

图——添加第二个节点

第二步:调整新节点和头结点的前后指针信息。

图——调整前后指针信息

添加后续数据情况和上述一致,LinkedList实例是没有容量限制的。

 

总结,addBefore(E e,Entry<E> entry)实现在entry之前插入由e构造的新节点。而add(E e)实现在header节点之前插入由e构造的新节点。为了便于理解,下面给出插入节点的示意图。


然后是get方法

 public E get(int paramInt)
  {
    return entry(paramInt).element;
  }

 private Entry<E> entry(int paramInt)
  {
    if ((paramInt < 0) || (paramInt >= this.size)) {
      throw new IndexOutOfBoundsException("Index: " + paramInt + ", Size: " + this.size);
    }
    Entry localEntry = this.header;
    int i;
    if (paramInt < this.size >> 1) {
      for (i = 0; i <= paramInt; i++) {
        localEntry = localEntry.next;
      }
    } else {
      for (i = this.size; i > paramInt; i--) {
        localEntry = localEntry.previous;
      }
    }
    return localEntry;
  }

至此,我们可以得出LinkedList的特点

1. 底层是双向循环链表

2.有序,可重复

3.查找慢,增删快(和ArrayList相比)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值