經(jīng)常為了面試要記住LinkedList和ArrayList的區(qū)別,比如存取速度的比較。如果不了解他們的實(shí)現(xiàn)方式,不看源碼,記住了也是死記硬背,容易忘記,沒(méi)有太大的意義。這里講的是LinkedList,暫時(shí)不講ArrayList,從源碼了解LinkedList的實(shí)現(xiàn)方式以及操作方法的實(shí)現(xiàn),才能更全面了解LinkedList。
LinkedList是基于雙向鏈表的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的,所以必須要知道掌握雙向鏈表這種數(shù)據(jù)結(jié)構(gòu),如果對(duì)雙向鏈表不了解的話得先去學(xué)一下,比較容易理解的一種數(shù)據(jù)結(jié)構(gòu)。學(xué)習(xí)了雙向鏈表看LinkedList的源碼會(huì)更輕松一些,從 源碼了解LinkedList的性質(zhì),才能更加了解LinkedList,知道在什么場(chǎng)合更加適合使用LinkedList。別啰嗦了,看源碼:
public class LinkedList<E> extends AbstractSequentialList<E> implements
List<E>, Deque<E>, Queue<E>, Cloneable, Serializable {
....
}
LinkedList可以指定一個(gè)泛型(一般我們都會(huì)這么做),具有多個(gè)實(shí)現(xiàn),源碼大部分邏輯都是在實(shí)現(xiàn)這些接口的方法,從它們的名字我們可以看出除了可以當(dāng)作鏈表來(lái)操作外,它還可以當(dāng)作棧,隊(duì)列和雙端隊(duì)列來(lái)使用。
來(lái)看構(gòu)造函數(shù)
//記錄LinkedList的大小,即LinkedList有多少個(gè)節(jié)點(diǎn)(或者說(shuō)元素)
transient int size = 0;
//一個(gè)空的節(jié)點(diǎn)
transient Link<E> voidLink;
/**
* Constructs a new instance of {@code LinkedList} that holds all of the
* elements contained in the specified {@code collection}. The order of the
* elements in this new {@code LinkedList} will be determined by the
* iteration order of {@code collection}.
*
* @param collection
* the collection of elements to add.
*/
//這個(gè)帶參數(shù)的構(gòu)造函數(shù)的this()方法是調(diào)用下面的那個(gè)構(gòu)造函數(shù),之后將參數(shù)collection跟voidLink(一個(gè)空節(jié)點(diǎn),在第二個(gè)構(gòu)造函數(shù)里創(chuàng)建出來(lái)的)
//鏈接起來(lái),addAll(collection)后面講解
public LinkedList(Collection<? extends E> collection) {
this();
addAll(collection);
}
/**
* Constructs a new empty instance of {@code LinkedList}(創(chuàng)建一個(gè)空的實(shí)例)
*/
public LinkedList() {
voidLink = new Link<E>(null, null, null);
voidLink.previous = voidLink;
voidLink.next = voidLink;
}
//創(chuàng)建節(jié)點(diǎn)的靜態(tài)內(nèi)部類
private static final class Link<ET> {
//ET其實(shí)就是E這個(gè)泛型,從上面的構(gòu)造函數(shù)可以看出,這個(gè)字段就是元素
ET data;
//這兩個(gè)字段分別是當(dāng)前節(jié)點(diǎn)的頭和尾,分布存儲(chǔ)的是前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn),雙向鏈表數(shù)據(jù)結(jié)構(gòu)的每個(gè)節(jié)點(diǎn)都是有這三個(gè)東西組成的
Link<ET> previous, next;
Link(ET o, Link<ET> p, Link<ET> n) {
data = o;
previous = p;
next = n;
}
}
從第二個(gè)構(gòu)造函數(shù)我們知道new出一個(gè)LinkedList的時(shí)候,這個(gè)實(shí)例里面只有一個(gè)空的節(jié)點(diǎn)voidLink ,一般我們會(huì)有個(gè)add(int location, E object)函數(shù)添加元素到指定位置,所以接著看add(E object)函數(shù)添加元素到最后一個(gè)節(jié)點(diǎn)的后面,addAll(int location, Collection<? extends E> collection)函數(shù)添加Collection到指定位置,addAll(Collection<? extends E> collection)函數(shù)添加Collection到最后一個(gè)節(jié)點(diǎn)的后面。addFirst(E object)添加元素到第一個(gè)節(jié)點(diǎn)的前面,addLast(E object)添加元素到最后一個(gè)節(jié)點(diǎn)后面。這些是所有添加元素的方法。分別看下他們的邏輯,都是相似的。理解了其中一個(gè)其他都好理解。
/**
* Inserts the specified object into this {@code LinkedList} at the
* specified location. The object is inserted before any previous element at
* the specified location. If the location is equal to the size of this
* {@code LinkedList}, the object is added at the end.
*上面的意思就是將指定的object 插入到指定的位置,之前位置的object 以及它后面的object 的位置的都會(huì)加1
*注意最后一句話的意思是如果這個(gè)LinkedList的size等于location ,那么意思就是插入到最后一個(gè)位置。不允許location >size,location<0
*
* @param location
* the index at which to insert.
* @param object
* the object to add.
* @throws IndexOutOfBoundsException
* if {@code location < 0 || location > size()}
*/
@Override
public void add(int location, E object) {
if (location >= 0 && location <= size) {
Link<E> link = voidLink;
if (location < (size / 2)) {
for (int i = 0; i <= location; i++) {
link = link.next;
}
} else {
for (int i = size; i > location; i--) {
link = link.previous;
}
}
Link<E> previous = link.previous;
Link<E> newLink = new Link<E>(object, previous, link);
previous.next = newLink;
link.previous = newLink;
size++;
modCount++;
} else {
throw new IndexOutOfBoundsException();
}
}
方法注釋可以了解到大概,分析代碼才能更細(xì)節(jié)的了解,可以看到location 必須大于等于0且小于等于sise,否則會(huì)拋出異常,滿足條件后需要?jiǎng)?chuàng)建一個(gè)節(jié)點(diǎn)引用,先指向voidLink這個(gè)空節(jié)點(diǎn),因?yàn)槲覀冃枰柚鷙oidLink和size才能找到一個(gè)目標(biāo)節(jié)點(diǎn),該目標(biāo)節(jié)點(diǎn)起著關(guān)鍵的作用,先看if和else語(yǔ)句,判斷插入位置區(qū)域鏈表的哪一邊,如果是左邊的話則進(jìn)入if條件的for循環(huán)從第一個(gè)位置開(kāi)始找目標(biāo)節(jié)點(diǎn),首先我們必須知道voidLink.next永遠(yuǎn)的是當(dāng)前的第一個(gè)節(jié)點(diǎn),等看完了所有添加元素的函數(shù)邏輯的時(shí)候就明白了。如果進(jìn)入else條件,邏輯也是一樣的,只是從尾部開(kāi)始查找目標(biāo)節(jié)點(diǎn)。找到目標(biāo)節(jié)點(diǎn)之后就很簡(jiǎn)單了,把目標(biāo)節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)的next指向newLink (新插入節(jié)點(diǎn)),目標(biāo)節(jié)點(diǎn)的previous 指向newLink ,這樣就完成了拼接,目標(biāo)節(jié)點(diǎn)的位置被新節(jié)點(diǎn)占據(jù)著,著目標(biāo)節(jié)點(diǎn)以及后面的節(jié)點(diǎn)的位置都各自加1,此時(shí)我們還要加size+1,modCount+1只是一個(gè)統(tǒng)計(jì)修改的次數(shù)。邏輯還是比較簡(jiǎn)單的,后面的幾個(gè)添加元素的方法都是大同小異的。
add(E object)在尾部添加節(jié)點(diǎn),更加簡(jiǎn)單目標(biāo)節(jié)點(diǎn)哦度不用找,因?yàn)関oidLink.previous就是最后一個(gè)節(jié)點(diǎn),跟voidLink.next永遠(yuǎn)的是當(dāng)前的第一個(gè)節(jié)點(diǎn)永遠(yuǎn)是第一個(gè)節(jié)點(diǎn)相呼應(yīng):
/**
* Adds the specified object at the end of this {@code LinkedList}.
*
* @param object
* the object to add.
* @return always true
*/
@Override
public boolean add(E object) {
return addLastImpl(object);
}
private boolean addLastImpl(E object) {
Link<E> oldLast = voidLink.previous;
Link<E> newLink = new Link<E>(object, oldLast, voidLink);
voidLink.previous = newLink;
oldLast.next = newLink;
size++;
modCount++;
return true;
}
其他幾個(gè)添加元素的函數(shù)都是按照這種套路執(zhí)行的,不必每一個(gè)都進(jìn)行分析了,但每個(gè)人都有必要去看一遍的。
與添加對(duì)應(yīng)的方法就是刪除了,remove(int location),removeFirst(),removeLast(),方法名可以看出什么意思了,刪除的套路前半部分也是一樣的,目標(biāo)節(jié)點(diǎn),然后根據(jù)目標(biāo)節(jié)點(diǎn)找到他前后的節(jié)點(diǎn),讓前一個(gè)節(jié)點(diǎn)的next指向后一個(gè)節(jié)點(diǎn),后一個(gè)節(jié)點(diǎn)的previous指向前一個(gè)節(jié)點(diǎn)。還有一個(gè)remove(Object object)方法收回復(fù)雜一些,后面單獨(dú)講,先看remove(int location)(removeFirst(),removeLast()與removeFirst(),removeLast()套路相似)
/**
* Removes the object at the specified location from this {@code LinkedList}.
*
* @param location
* the index of the object to remove
* @return the removed object
* @throws IndexOutOfBoundsException
* if {@code location < 0 || location >= size()}
*/
@Override
public E remove(int location) {
if (location >= 0 && location < size) {
Link<E> link = voidLink;
if (location < (size / 2)) {
for (int i = 0; i <= location; i++) {
link = link.next;
}
} else {
for (int i = size; i > location; i--) {
link = link.previous;
}
}
Link<E> previous = link.previous;
Link<E> next = link.next;
previous.next = next;
next.previous = previous;
size--;
modCount++;
return link.data;
}
throw new IndexOutOfBoundsException();
}
remove(Object object)源碼
@Override
public boolean remove(Object object) {
return removeFirstOccurrenceImpl(object);
}
private boolean removeFirstOccurrenceImpl(Object o) {
Iterator<E> iter = new LinkIterator<E>(this, 0);
return removeOneOccurrence(o, iter);
}
private boolean removeOneOccurrence(Object o, Iterator<E> iter) {
while (iter.hasNext()) {
E element = iter.next();
if (o == null ? element == null : o.equals(element)) {
iter.remove();
return true;
}
}
return false;
}
從源碼看到的調(diào)用到最后removeOneOccurrence(Object o, Iterator<E> iter)函數(shù)的iter.remove()防止執(zhí)行了移除工作,這個(gè)方法里面調(diào)用了LinkIterator的hasNext()、next()、remove()三個(gè)方法,進(jìn)去看他們的源碼看下到底做了什么工作
public boolean hasNext() {
return link.next != list.voidLink;
}
public ET next() {
if (expectedModCount == list.modCount) {
LinkedList.Link<ET> next = link.next;
if (next != list.voidLink) {
lastLink = link = next;
pos++;
return link.data;
}
throw new NoSuchElementException();
}
throw new ConcurrentModificationException();
}
public void remove() {
if (expectedModCount == list.modCount) {
if (lastLink != null) {
Link<ET> next = lastLink.next;
Link<ET> previous = lastLink.previous;
next.previous = previous;
previous.next = next;
if (lastLink == link) {
pos--;
}
link = previous;
lastLink = null;
expectedModCount++;
list.size--;
list.modCount++;
} else {
throw new IllegalStateException();
}
} else {
throw new ConcurrentModificationException();
}
}
hasNext()判斷當(dāng)前節(jié)點(diǎn)(就是voidLink,從構(gòu)造函數(shù)看出來(lái)的)的下一個(gè)節(jié)點(diǎn)是否為空,next()返回下一個(gè)節(jié)點(diǎn)的data,remove()方法終于是我們熟悉的背影了,主要邏輯就在這里了。