LinkList源码分析
一、
(1)LinkList是一个双向循环链表,其内部有一个entry类来对它维护。
private transient Entry<E> header = new Entry<E>(null,null,null);
在Entry中就包含链表的三个属性,previous、next、element。
(2)具体的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:指向当前节点的前一个节点
next:指向当前节点的后一个节点
(这就是双向链表的数据结构)
return remove(header.next);
}
引出
private E remove(Entry<E> e) {
throw new NoSuchElementException(); //如果是头结点的话,抛出异常
e.previous.next = e.next;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
}
可以看出remove(Entry<E> e)是一个私有方法,所有我们是没法直接去调用此方法
的,该方法就是为LinkedList本身服务的,因为LinkedList是由Entry维护,Entry即我们
所说的节点,删除它的操作很简单,只要把当前节点的前一个节点的next指向当前节点
的下一个节点(e.previous.next = e.next;),然后当前节点的下一个节点的previous
指向当前节点的前一个节点(e.next.previous = e.previous;),最后把当前节点的
next,previous、element置为null,以便GC回收(e.next = e.previous = null;
e.element = null;)删除操作就完成了,我们从中可以看出他的时间复杂度仅为O(1)
,也就是说删除LinkedList中第一个元素和最后一个元素的时间复杂的仅为O(1),所以他的
操作是非常快的。
public boolean remove(Object o) {
if (o==null) {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (e.element==null) {
remove(e);
return true;
}
}
} else {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (o.equals(e.element)) { //先找到e元素的位置,然后调用remove删除
remove(e);
return true;
}
}
}
return false;
}
发现这方法的内部又调用了一个前面已经分析过的remove(Entry<E> e)方法,
在这个方法中却多了一个for循环,他要从一个节点开始找,直到找到那个值于传入的
参数值相等,我们可以看出他的时间复杂度就不是我们普遍认为的O(1) 了,而变成了
O(n),之所以这样是LinkedList作为一个通用性的链表结构,由Entry去维护该数据结构,
而不是拿我们直接保存在 LinkedList的值,相当于做了一层包装,所以你要删除某个
值,你还得去找到那个对应的Entry对象。
(5)删除某个指定位置元素的方法,跟踪一下entry(index)方法
if (index < 0 || index >= size) //不符合的index要抛异常
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry<E> e = header;
if (index < (size >> 1)) { //index小于size的一半
e = e.next; //e由header依次向(size/2)移动过去
} else {
for (int i = size; i > index; i--) //e大于(size/2)
e = e.previous; //所以e从最后一个往前移动
}
return e;
}
原来删除某个位置的元素还是这样实现的,为了找到index位置的 Entry元素,它根据
index元素与LinkedList大小的一半(size>>1)做了次比较,如果比size/2小,它就由前往
后找,如果比size/2大,它就从后往前找,并不是我们所想的一味的又前往后找,这样
一来,除去比较所消耗的时间,他的时间复杂度为O(n/2)。
(6)添加的操作就没那么复杂了
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;
}
这方法的意思是说,把e对应的节点添加到entry的前面,首先构造newEntry对象,即
新节点,然后是新节点的前一个节点的next指向当前的新节点,当前新节点的下一个元
素的previous也指向新节点.