jdk1.6集合源碼閱讀之LinkedList

      若是說ArrayList是基於數組實現的List,那麼LinkedList是基於鏈表實現的List。java

1.定義

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

        能夠看獲得LinkedList繼承了AbstractSequentialList。實現了List,Deque. 後面兩個和ArrayList同樣,說明能夠被克隆和序列化。數組

          而AbstractSequentialList基礎自AbstractList,並且還從新實現了get,set,add,remove,等等方法。數據結構

AbstractSequentialList的代碼以下:函數

package java.util;


public abstract class AbstractSequentialList<E> extends AbstractList<E> {
   
    protected AbstractSequentialList() {
    }

   
    public E get(int index) {
        try {
            return listIterator(index).next();
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }

 
    public E set(int index, E element) {
	try {
	    ListIterator<E> e = listIterator(index);
	    E oldVal = e.next();
	    e.set(element);
	    return oldVal;
	} catch (NoSuchElementException exc) {
	    throw new IndexOutOfBoundsException("Index: "+index);
	}
    }

   
    public void add(int index, E element) {
	try {
	    listIterator(index).add(element);
	} catch (NoSuchElementException exc) {
	    throw new IndexOutOfBoundsException("Index: "+index);
	}
    }

    public E remove(int index) {
	try {
	    ListIterator<E> e = listIterator(index);
	    E outCast = e.next();
	    e.remove();
	    return outCast;
	} catch (NoSuchElementException exc) {
	    throw new IndexOutOfBoundsException("Index: "+index);
	}
    }


   
    public boolean addAll(int index, Collection<? extends E> c) {
	try {
	    boolean modified = false;
	    ListIterator<E> e1 = listIterator(index);
	    Iterator<? extends E> e2 = c.iterator();
	    while (e2.hasNext()) {
		e1.add(e2.next());
		modified = true;
	    }
	    return modified;
	} catch (NoSuchElementException exc) {
	    throw new IndexOutOfBoundsException("Index: "+index);
	}
    }


 
    public Iterator<E> iterator() {
        return listIterator();
    }

    
    public abstract ListIterator<E> listIterator(int index);
}

而Dqueue接口 是一個雙向隊列,也就是既能夠先入先出,又能夠先入後出,再直白一點就是既能夠在頭部添加元素又在尾部添加元素,既能夠在頭部獲取元素又能夠在尾部獲取元素。看下Deque的定義this

public interface Deque<E> extends Queue<E>

 

 

2.底層存儲   

private transient Entry<E> header = new Entry<E>(null, null, null);
 private transient int size = 0;

size和ArrayList裏面的size同樣,記錄容器元素的個數。那這個Entry類型的變量header是個什麼鬼。spa

Entry是個內部類,來描述鏈表的節點的信息,代碼以下:.net

//描述鏈表節點的類
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;
	}
}

能夠看得出ArrayList底層是採用雙向鏈表來實現的。指針

數據結構雙向鏈表是包含兩個指針的,pre指向前一個節點,next指向後一個節點,可是第一個節點head的pre指向null,最後一個節點的tail指向null。code

3.構造函數

//默認構造
public LinkedList() {
    鏈表的頭和尾都指向了本身
    header.next = header.previous = header;
}

//將一個集合的元素來構造本身,這些元素按其 collection 的迭代器返回的順序排列
public LinkedList(Collection<? extends E> c) {
	this();
	addAll(c);
}

從默認構造函數能夠看得出這是一個雙向循環鏈表,若是是雙向不循環鏈表的話,應該是:對象

header.next=header.previous=null。

4.增長

        有8個增長,5個增長是實現Dqueue裏面的添加函數,其他3個是實現List接口裏面的添加函數,而後實現Dqueue基本是調用實現List裏面的添加函數,因此咱們使用的時候直接調用List裏面的添加函數便可。能夠少壓一次棧。

//這5個add是LinkedList實現Dqueue裏面的添加函數,其實都是調用實現List接口裏面的函數

//將元素加到第一個,能夠看獲得,是加到header的後面,由於header就是一個空頭,裏面沒有存儲元素
public void addFirst(E e) {
	addBefore(e, header.next);
}
//將元素加列表的尾部
public void addLast(E e) {
	addBefore(e, header);
}

//封裝鏈表插入操做,分兩步走
//1.其實就是在構造的時候,本身的netx和previous指好
//2.本身的前節點的next指向本身,本身後結點的pre指向本身。
private Entry<E> addBefore(E e, Entry<E> entry) {
	Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);

    //本身的前節點的next指向本身
	newEntry.previous.next = newEntry;
    //本身後結點的pre指向本身
	newEntry.next.previous = newEntry;
    //記數加1
	size++;
	modCount++;
	return newEntry;
}

 public boolean offer(E e) {
        return add(e);
}


public boolean offerFirst(E e) {
        addFirst(e);
        return true;
}

   
public boolean offerLast(E e) {
        addLast(e);
        return true;
}

//下面三個增長是實現List接口裏面的添加函數
//將元素加列表的尾部
public boolean add(E e) {
	addBefore(e, header);
    return true;
}

//添加一個集合元素到list中
public boolean addAll(Collection<? extends E> c) {
         //其實仍是調用在指定位置添加一個集合到list中
        return addAll(size, c);
}

//在指定位置添加一個集合元素到list中   
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++;

        //找出要插入元素的先後節點

        //找出其後節點,若是位置和size大小相等(爲何不是size-1,由於header佔了一位),則他的下一個節點是header
        //不然要查找index位置的節點,這個就是他的後一個節點
        Entry<E> successor = (index==size ? header : entry(index));
        //他的前一個節點,就是index位置的前一個節點。
        Entry<E> predecessor = successor.previous;
	    for (int i=0; i<numNew; i++) {
            Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);
            //將它前一個節點的next指向本身
            predecessor.next = e;
            //後面的元素插入到這個元素的後面
            predecessor = e;
        }
        //將index位置的節點的前指針指向本身這樣就完成了鏈表的操做。
        successor.previous = predecessor;

        size += numNew;
        return true;
}

雙向鏈表的增長比起數組的增長稍微是要麻煩的理解一點,本身畫圖應該不難理解,總結起來,就是一句話主要就是插入會改變前節點的next和後一節點的pre,主要把前一節點的next指向本身,後一節點的pre指向本身,便可。

5.刪除

//刪除第一個容器元素
public E removeFirst() {
	return remove(header.next);
}

//刪除最後一個容器元素
public E removeLast() {
	return remove(header.previous);
}

//刪除指定的容器元素
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 {
            //循環遍歷節點,而後找到節點,而後刪除。時間複雜度O(n)
            for (Entry<E> e = header.next; e != header; e = e.next) {
                if (o.equals(e.element)) {
                    remove(e);
                    return true;
                }
            }
        }
        return false;
}

//刪除指定位置的容器元素
public E remove(int index) {
    //entry(index)是找到這個節點
    return remove(entry(index));
}

//刪除指定的節點,供本身調用
private E remove(Entry<E> e) {
	if (e == header)
	    throw new NoSuchElementException();

        E result = e.element;
    //將本身的前一節點的後指針執行本身的後一節點
	e.previous.next = e.next;
    //講本身後一節點的前指針指向本身的前節點
    //講本身置爲null,給gc回收
	e.next.previous = e.previous;
        e.next = e.previous = null;
        e.element = null;
    //大小減一
	size--;
	modCount++;
        return result;
}

刪除則要簡單一點,刪除了本身以後,主要

將本身的前一節點的後指針執行本身後一節點
講本身後一節點的前指針指向本身的前節點

並且能夠看到remove(Object o)時間負責度爲O(n),而remove(int)的時間複雜度度爲O(n/2),由於裏面用到了二分查找的辦法。因此刪除的時候要注意了,要選用正確的方法刪除。(其實我轉載了一篇博客專門介紹這個LinkenList的侷限。)因此之前所說的增刪快的刪有時也是很慢的。

6.修改容器元素的值

public E set(int index, E element) {
        Entry<E> e = entry(index);
        E oldVal = e.element;
        e.element = element;
        return oldVal;
}

這個比較簡單,查找到,而後修改便可

7.查找

         咱們知道鏈表和數組相比,查找比數組要慢的很是多,數組直接定位,而鏈表每次咱們只能拿到一個頭部,因此無論找什麼,咱們都要從頭開始遍歷起,而LinkedList使用了雙向循環鏈表,這樣遍歷起來就會快不少,既能夠從頭日後找,又能夠從後往前找。直到找到index位置。

//查找指定index的鏈表裏面元素
public E get(int index) {
        return entry(index).element;
}

//這個方法很重要,基本上查找都是使用這個方法來進行查找。
//這兒就顯示雙向循環鏈表的好處,既能夠從頭日後遍歷,又能夠從後往前遍歷
//這兒使用了二分查找的方法,效率要高不少
private Entry<E> entry(int index) {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException("Index: "+index+
                                                ", Size: "+size);
        Entry<E> e = header;
        //若是下標小於size的通常,就從頭日後遍歷,找到元素
        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;
}

 到這裏咱們明白,基於雙向循環鏈表實現的LinkedList,經過索引Index的操做時低效的,index所對應的元素越靠近中間所費時間越長。而向鏈表兩端插入和刪除元素則是很是高效的(若是不是兩端的話,都須要對鏈表進行遍歷查找)。

8.其他的LinkedList的操做

8.1 查找元素是否在容器中

public boolean contains(Object o) {
        return indexOf(o) != -1;
}

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;
}

public int lastIndexOf(Object o) {
        int index = size;
        if (o==null) {
            for (Entry e = header.previous; e != header; e = e.previous) {
                index--;
                if (e.element==null)
                    return index;
            }
        } else {
            for (Entry e = header.previous; e != header; e = e.previous) {
                index--;
                if (o.equals(e.element))
                    return index;
            }
        }
        return -1;
}

要遍歷,低效。

打完收工。

相關文章
相關標籤/搜索