LinkedList源码解析

xiaoxiao2021-02-28  88

首先放出类图

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

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相比)

转载请注明原文地址: https://www.6miu.com/read-97278.html

最新回复(0)