LinkedList中存储数据的基本结构为java.util.LinkedList.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;
}
}
其中:previous指向前一个Entry,next指向后一个Entry,element存储当前对象的具体数据E,这个很好理解
那么第一个元素的previous指向哪里?最后一个元素的next又指向哪里?
其实每个LinkedList实例中都包涵有一个叫做header的元素,该元素即作为头又作为尾,使LinkedList的元素首尾相接,形成一个"环"
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;
/**
* Constructs an empty list.
*/
public LinkedList() {
header.next = header.previous = header;
}
header元素中的previous和next都指向自身,其中element为null
简单的看一下add方法
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
addBefore(e, header);
return true;
}
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方法中第二个元素(entry)是header,根据Entry的构造方法可以看出,新加入的nextEntry元素的next会指向header,previous指向header的previous,而header中的previous默认指向自身,在加入元素后则指向最后一个元素
(指针确实是把双刃剑,这里每个对象两个指针,互相指来指去,让人有点头晕...)
下面通过实际代码检验下:
通过反射可以清楚的看到LinkedList中Entry的指向关系
try {
LinkedList<String> ll = new LinkedList<String>();
ll.add("0001");
ll.add("0002");
Class<LinkedList> clazz = LinkedList.class;
//通过反射获取header属性
Field headerField = clazz.getDeclaredField("header");
headerField.setAccessible(true);
Object header = headerField.get(ll);
Class entryClazz = header.getClass();
//获取header元素中的element/next/previous
Field ele = entryClazz.getDeclaredField("element");
Field next = entryClazz.getDeclaredField("next");
Field pre = entryClazz.getDeclaredField("previous");
ele.setAccessible(true);
next.setAccessible(true);
pre.setAccessible(true);
for (int i = 0; i < 3; i++) {
//当前Entry对象
System.out.print(header + "[");
//当前Entry对象previous指向的对象
System.out.print(pre.get(header) + " -> ");
//当前Entry对象存储的值
System.out.print(ele.get(header) + " -> ");
//当前Entry对象next指向的对象
System.out.print(next.get(header));
System.out.println("]");
header = next.get(header);
}
} catch (Exception e) {
e.printStackTrace();
}
首先我们使用一个空的LinkedList,循环打印三次:
java.util.LinkedList$Entry@38910040[java.util.LinkedList$Entry@38910040 -> null -> java.util.LinkedList$Entry@38910040]
java.util.LinkedList$Entry@38910040[java.util.LinkedList$Entry@38910040 -> null -> java.util.LinkedList$Entry@38910040]
java.util.LinkedList$Entry@38910040[java.util.LinkedList$Entry@38910040 -> null -> java.util.LinkedList$Entry@38910040]
可以清楚看到只有一个Entry对象(38910040)header,并指向自身相成一个环形结构
接下来插入一个元素"001":
java.util.LinkedList$Entry@19b8e059[java.util.LinkedList$Entry@38910040 -> null -> java.util.LinkedList$Entry@38910040]
java.util.LinkedList$Entry@38910040[java.util.LinkedList$Entry@19b8e059 -> 0001 -> java.util.LinkedList$Entry@19b8e059]
java.util.LinkedList$Entry@19b8e059[java.util.LinkedList$Entry@38910040 -> null -> java.util.LinkedList$Entry@38910040]
可以看到有两个Entry,分别是header(19b8e059)和存放"001"的Entry(38910040),回过头来再看addBefore方法,就可以理解为:
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
newEntry.element = e;
newEntry.next = header;
newEntry.previous = header.previous;
newEntry.previous.next = newEntry;
header.previous = newEntry;
newEntry.next.previous = newEntry;
header.next = newEntry;
这时header和我们新增的元素会首尾相接,形成环
最后我们再加入一个元素"002",循环5次:
java.util.LinkedList$Entry@38910040[java.util.LinkedList$Entry@37a786c3 -> null -> java.util.LinkedList$Entry@578088c0]
java.util.LinkedList$Entry@578088c0[java.util.LinkedList$Entry@38910040 -> 0001 -> java.util.LinkedList$Entry@37a786c3]
java.util.LinkedList$Entry@37a786c3[java.util.LinkedList$Entry@578088c0 -> 0002 -> java.util.LinkedList$Entry@38910040]
java.util.LinkedList$Entry@38910040[java.util.LinkedList$Entry@37a786c3 -> null -> java.util.LinkedList$Entry@578088c0]
java.util.LinkedList$Entry@578088c0[java.util.LinkedList$Entry@38910040 -> 0001 -> java.util.LinkedList$Entry@37a786c3]
可以看到有三个Entry对象38910040,578088c0,37a786c3,第一个对象的previous指向header,第二个(最后一个)对象的next指向header,结果与我们预期的一样
另外header在遍历元素过程也有用到,从[header.next, header)不包含header。可以通过判断“当前对象是否等于header”来确定是否遍历完成