準(zhǔn)備:
LinkedList是基于鏈表(雙向循環(huán)鏈表)結(jié)構(gòu)的一種List耻矮,在分析LinkedList源碼之前我們先看看鏈表
1、鏈表概念
鏈表是由一系列非連續(xù)的節(jié)點(diǎn)組成的存儲(chǔ)結(jié)構(gòu)纫普,簡(jiǎn)單分下類的話默刚,鏈表又分為單向鏈表和雙向鏈表媚创,而單向/雙向鏈表又可以分為循環(huán)鏈表和非循環(huán)鏈表丹允,下面簡(jiǎn)單就這四種鏈表進(jìn)行圖解說(shuō)明。
1.1携茂、單向鏈表
單向鏈表就是通過(guò)每個(gè)結(jié)點(diǎn)的指針指向下一個(gè)結(jié)點(diǎn)從而鏈接起來(lái)的結(jié)構(gòu)你踩,最后一個(gè)節(jié)點(diǎn)的next指向null。
1.2讳苦、單向循環(huán)鏈表
單向循環(huán)鏈表和單向列表的不同是带膜,最后一個(gè)節(jié)點(diǎn)的next不是指向null,而是指向head節(jié)點(diǎn)鸳谜,形成一個(gè)“環(huán)”膝藕。
1.3、雙向鏈表
從名字就可以看出咐扭,雙向鏈表是包含兩個(gè)指針的芭挽,pre指向前一個(gè)節(jié)點(diǎn),next指向后一個(gè)節(jié)點(diǎn)蝗肪,但是第一個(gè)節(jié)點(diǎn)head的pre指向null袜爪,最后一個(gè)節(jié)點(diǎn)的tail指向null。
1.4薛闪、雙向循環(huán)鏈表
雙向循環(huán)鏈表和雙向鏈表的不同在于辛馆,第一個(gè)節(jié)點(diǎn)的pre指向最后一個(gè)節(jié)點(diǎn),最后一個(gè)節(jié)點(diǎn)的next指向第一個(gè)節(jié)點(diǎn)豁延,也形成一個(gè)“環(huán)”怀各。而LinkedList就是基于雙向循環(huán)鏈表設(shè)計(jì)的。
更形象的解釋下就是:雙向循環(huán)鏈表就像一群小孩手牽手圍成一個(gè)圈术浪,第一個(gè)小孩的右手拉著第二個(gè)小孩的左手,第二個(gè)小孩的左手拉著第一個(gè)小孩的右手寿酌。胰苏。。最后一個(gè)小孩的右手拉著第一個(gè)小孩的左手醇疼。
附圖:
一 :結(jié)構(gòu)關(guān)系
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable{
可以看出LinkedList 繼承AbstractSequentialList 抽象類硕并,實(shí)現(xiàn)List法焰,Deque,Cloneable倔毙,Serializable 幾個(gè)接口埃仪,AbstractSequentialList 繼承 AbstractList,是對(duì)其中方法的再抽象陕赃,其主要作用是最大限度地減少了實(shí)現(xiàn)受“連續(xù)訪問”數(shù)據(jù)存儲(chǔ)(如鏈接列表)支持的此接口所需的工作卵蛉,簡(jiǎn)單說(shuō)就是,如果需要快速的添加刪除數(shù)據(jù)等么库,用AbstractSequentialList抽象類傻丝,若是需要快速隨機(jī)的訪問數(shù)據(jù)等用AbstractList抽象類(詳細(xì)說(shuō)明會(huì)在iterator 分析中進(jìn)行解釋)。
注意:
Deque 是一個(gè)雙向隊(duì)列诉儒,也就是既可以先入先出葡缰,又可以先入后出,再直白一點(diǎn)就是既可以在頭部添加元素又在尾部添加元素忱反,既可以在頭部獲取元素又可以在尾部獲取元素泛释。看下Deque的定義:
public interface Deque<E> extends Queue<E> {
void addFirst(E e);
boolean offerFirst(E e);
boolean offerLast(E e);
E removeFirst();
E removeLast();
E pollFirst();
E pollLast();
E getFirst();
E getLast();
E peekFirst();
E peekLast();
boolean removeFirstOccurrence(Object o);
boolean removeLastOccurrence(Object o);
// *** Queue methods ***
boolean add(E e);
boolean offer(E e);
E remove();
E poll();
E element();
E peek();
// *** Stack methods ***
void push(E e);
E pop();
// *** Collection methods ***
boolean remove(Object o);
boolean contains(Object o);
public int size();
Iterator<E> iterator();
Iterator<E> descendingIterator();
}
二 :介紹
(1):LinkedList是基于雙向循環(huán)鏈表實(shí)現(xiàn)的温算,除了可以當(dāng)做鏈表來(lái)操作外怜校,它還可以當(dāng)做棧、隊(duì)列和雙端隊(duì)列來(lái)使用米者。
(2):是一種鏈表類型的數(shù)據(jù)結(jié)構(gòu)韭畸,支持高效的插入和刪除操作,同時(shí)也實(shí)現(xiàn)了Deque接口蔓搞,使得LinkedList類也具有隊(duì)列的特性胰丁。LinkedList類的底層實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)是一個(gè)雙端的鏈表。
(3): 不是線程安全的
(4):LinkedList實(shí)現(xiàn)了Serializable接口喂分,因此它支持序列化锦庸,能夠通過(guò)序列化傳輸,實(shí)現(xiàn)了Cloneable接口蒲祈,能被克隆甘萧。
三 :重要知識(shí)點(diǎn)
(1):重要屬性
(2):LinkedList的創(chuàng)建:即構(gòu)造器
(3):重點(diǎn)內(nèi)部類以及重點(diǎn)方法
(4):線程安全與否以及解決方案
四 :源碼淺析
4.1重要屬性:
//集合長(zhǎng)度
transient int size = 0;
//首節(jié)點(diǎn)
transient Node<E> first;
//尾節(jié)點(diǎn)
transient Node<E> last;
這里注意LinkedList的元素結(jié)構(gòu),是一個(gè)node元素梆掸,是一個(gè)雙向鏈表扬卷,我們看看node的構(gòu)成
// 雙向鏈表的節(jié)點(diǎn)所對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)。
// 包含3部分:上一節(jié)點(diǎn)酸钦,下一節(jié)點(diǎn)怪得,當(dāng)前節(jié)點(diǎn)值。
private static class Node<E> {
E item;// 當(dāng)前節(jié)點(diǎn)所包含的值
Node<E> next;// 下一個(gè)節(jié)點(diǎn)
Node<E> prev; // 上一個(gè)節(jié)點(diǎn)
/**
* 鏈表節(jié)點(diǎn)的構(gòu)造函數(shù)。
* 參數(shù)說(shuō)明:
* element —— 節(jié)點(diǎn)所包含的數(shù)據(jù)
* next —— 下一個(gè)節(jié)點(diǎn)
* previous —— 上一個(gè)節(jié)點(diǎn)
*/
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
注意上邊的node源碼徒恋,LinkedList類中有一個(gè)內(nèi)部私有類Node蚕断,這個(gè)類就代表雙端鏈表的節(jié)點(diǎn)Node。這個(gè)類有三個(gè)屬性入挣,分別是前驅(qū)節(jié)點(diǎn)亿乳,本節(jié)點(diǎn)的值,后繼結(jié)點(diǎn)径筏。
注意這個(gè)節(jié)點(diǎn)的初始化方法葛假,給定三個(gè)參數(shù),分別前驅(qū)節(jié)點(diǎn)匠璧,本節(jié)點(diǎn)的值桐款,后繼結(jié)點(diǎn)。這個(gè)方法將在LinkedList的實(shí)現(xiàn)中多次調(diào)用夷恍。
4.2創(chuàng)建一個(gè)LinkedList:
API提供了兩種創(chuàng)建LinkedList的構(gòu)造方法
(1): public LinkedList() //無(wú)參構(gòu)造創(chuàng)建
(2):public LinkedList(Collection<? extends E> c)//帶參構(gòu)造創(chuàng)建
4.3向LinkedList中添加元素:
LinkedList的添加方法有好幾個(gè)
(1):public boolean add(E e)
(1):public boolean addAll(Collection<? extends E> c)
(1):public void add(int index, E element)
(1):public void addFirst(E e)
(1):public void addLast(E e)
這里我們主要分析add(E e),以下是源碼:
//追加節(jié)點(diǎn)(尾部添加)
public boolean add(E e) {
linkLast(e);
return true;
}
//在鏈表尾部添加一個(gè)元素
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
增加元素的實(shí)現(xiàn)其實(shí)很簡(jiǎn)單魔眨,只要理解了雙向鏈表的存儲(chǔ)結(jié)構(gòu),掌握增加的核心邏輯就可以了酿雪。這里總結(jié)一下核心邏輯:1.將元素轉(zhuǎn)換為鏈表節(jié)點(diǎn)遏暴,2.增加該節(jié)點(diǎn)的前后引用(即pre和next分別指向哪一個(gè)節(jié)點(diǎn)),3.前后節(jié)點(diǎn)對(duì)該節(jié)點(diǎn)的引用(前節(jié)點(diǎn)的next指向該節(jié)點(diǎn)指黎,后節(jié)點(diǎn)的pre指向該節(jié)點(diǎn))朋凉。其實(shí)就是前后變換的互相指向關(guān)系
4.4從LinkedList中移除元素:
同樣LinkedList的刪除方法也有好幾個(gè)
(1):public boolean remove(Object o)
(2):public E remove()
(3):public E remove(int index)
(4):public E removeFirst()
(5):public E removeLast()
這里一樣,我們重點(diǎn)看public E remove()
//刪除并返回第一個(gè)節(jié)點(diǎn) 若LinkedList的大小為0,則拋出異常
public E remove() {
return removeFirst();
}
//移除首節(jié)點(diǎn)并返回該節(jié)點(diǎn)
public E removeFirst() {
final Node<E> f = first;
if (f == null) throw new NoSuchElementException();
return unlinkFirst(f);
}
//私有方法:解開首節(jié)點(diǎn)連接并移除該節(jié)點(diǎn)
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;//獲取首節(jié)點(diǎn)
final Node<E> next = f.next;//獲取其下一個(gè)節(jié)點(diǎn)
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;//容量--
modCount++;
return element;
}
上面對(duì)于鏈表增加元素總結(jié)了醋安,一句話就是“改變前后的互相指向關(guān)系”杂彭,刪除也是同樣的道理,由于節(jié)點(diǎn)被刪除吓揪,該節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)和下一個(gè)節(jié)點(diǎn)互相拉一下小手就可以了亲怠,注意的是“互相”,不能一廂情愿柠辞。
4.5修改LinkedList中某個(gè)元素:
//設(shè)置index位置對(duì)應(yīng)的節(jié)點(diǎn)的值為element
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
set方法看起來(lái)簡(jiǎn)單了很多团秽,只要修改該節(jié)點(diǎn)上的元素就好了,但是不要忽略了這里的 node(index)方法叭首,重點(diǎn)就是它习勤。
4.5查詢LinkedList中某個(gè)元素:
LinkedList提供了各種方式的get方法方便開發(fā)調(diào)用
(1):public E get(int index)
(2):public E getFirst()
(3):public E getLast()
主動(dòng)看public E get(int index)索引獲取源碼:
// 返回LinkedList指定位置的元素
public E get(int index) {
checkElementIndex(index);//檢查索引
return node(index).item;//注意,這里item才是真正的數(shù)據(jù)焙格,Node并不是
}
//返回指定元素索引中的(非空)節(jié)點(diǎn)
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
現(xiàn)在知道了图毕,LinkedList是通過(guò)從header開始index計(jì)為0,然后一直往下遍歷(next)眷唉,直到到底index位置吴旋。為了優(yōu)化查詢效率损肛,LinkedList采用了二分查找(這里說(shuō)的二分只是簡(jiǎn)單的一次二分),判斷index與size中間位置的距離荣瑟,采取從header向后還是向前查找。
到這里我們明白摩泪,基于雙向循環(huán)鏈表實(shí)現(xiàn)的LinkedList笆焰,通過(guò)索引Index的操作時(shí)低效的,index所對(duì)應(yīng)的元素越靠近中間所費(fèi)時(shí)間越長(zhǎng)见坑。而向鏈表兩端插入和刪除元素則是非常高效的(如果不是兩端的話嚷掠,都需要對(duì)鏈表進(jìn)行遍歷查找)。
4.6容量:
public int size() {
return size ;
}
public boolean isEmpty() {
return size() == 0;
}
4.6LinkedList實(shí)現(xiàn)的Deque雙端隊(duì)列 :
//將元素E添加到鏈表末尾
public boolean offer(E e) {
return add(e);
}
//刪除并返回第一個(gè)節(jié)點(diǎn) 若LinkedList的大小為0,則返回null
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//刪除并返回第一個(gè)節(jié)點(diǎn)
public E pop() {
return removeFirst();
}
//返回第一個(gè)節(jié)點(diǎn) 若LinkedList的大小為0,則返回null
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//獲取首節(jié)點(diǎn)
public E getFirst() {
final Node<E> f = first;
if (f == null) throw new NoSuchElementException();
return f.item;
}
//將e插入到雙向鏈表開頭
public void push(E e) {
addFirst(e);
}
//添加頭節(jié)點(diǎn)
public void addFirst(E e) {
linkFirst(e);
}
4.6迭代元素:
如果仔細(xì)觀察荞驴,你會(huì)發(fā)現(xiàn)在 LinkedList中是沒有自己實(shí)現(xiàn) iterator的不皆,如果這樣調(diào)用:
LinkedList<Integer> list = new LinkedList<>();
Iterator<Integer> it = list.iterator();
你會(huì)發(fā)現(xiàn)它調(diào)用的是 AbstractSequentialList中的 iterator(),返回的還是 AbstractList 中的 listIterator()熊楼,而在 LinkedList中實(shí)現(xiàn)了自己的 listIterator霹娄,因此如果進(jìn)行迭代,還是老老實(shí)實(shí)用 LinkedList中的 listIterator比較好鲫骗。LinkedList中還提供了逆序的 Iterator—— DescendingIterator犬耻,實(shí)際上它也只是簡(jiǎn)單的對(duì) ListIterator進(jìn)行了封裝:
public Iterator<E> iterator() {
return listIterator();
}
public ListIterator<E> listIterator() {
return listIterator(0);
}
public ListIterator<E> listIterator(final int index) {
rangeCheckForAdd(index);
return new ListItr(index);//最終返回的是一個(gè)Listltr的迭代器
}
看看Listltr源碼(是AbstractSequentialList的私有內(nèi)部類)
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public E previous() {
checkForComodification();
try {
int i = cursor - 1;
E previous = get(i);
lastRet = cursor = i;
return previous;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor-1;
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.set(lastRet, e);
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
AbstractList.this.add(i, e);
lastRet = -1;
cursor = i + 1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
五 :線程安全問題
LinkedList和arrayList一樣都是線程不安全的,如果在多線程程序中有多個(gè)線程訪問LinkedList的話會(huì)出現(xiàn)拋出ConcurrentModificationException
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
代碼中执泰,modCount記錄了LinkedList結(jié)構(gòu)被修改的次數(shù)枕磁。Iterator初始化時(shí),expectedModCount=modCount术吝。任何通過(guò)Iterator修改LinkedList結(jié)構(gòu)的行為都會(huì)同時(shí)更新expectedModCount和modCount计济,使這兩個(gè)值相等。通過(guò)LinkedList對(duì)象修改其結(jié)構(gòu)的方法只更新modCount排苍。所以假設(shè)有兩個(gè)線程A和B沦寂。A通過(guò)Iterator遍歷并修改LinkedList,而B纪岁,與此同時(shí)凑队,通過(guò)對(duì)象修改其結(jié)構(gòu),那么Iterator的相關(guān)方法就會(huì)拋出異常幔翰。這是相對(duì)容易發(fā)現(xiàn)的由線程競(jìng)爭(zhēng)造成的錯(cuò)誤漩氨。
測(cè)試代碼:
/**
* ClassName: TestLinkedList
* @author lvfang
* @Desc: TODO
* @date 2017-9-21
*/
public class TestLinkedList implements Runnable {
private LinkedList<Integer> list = null;
public TestLinkedList(LinkedList<Integer> list){
this.list = list;
}
@Override
public void run() {
for(int i=0;i<10;i++) list.add(i);
System.out.println(list.size());
}
/**
* LinkedList線程安全測(cè)試
* @param args
*/
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
//啟動(dòng)5個(gè)線程,每個(gè)線程向集合中添加10條數(shù)據(jù)遗增,最終應(yīng)該是50
for (int i = 0; i < 5; i++) {
new Thread(new TestLinkedList(list)).start();
}
}
}
解決方案
方案一:Collections類的同步list方法
List<String> linkedList = Collections.synchronizedList(new LinkedList<String>())
方案二:ConcurrentLinkedQueue代替linkedList
六 :總結(jié)(LinkedList和ArrayList的大致區(qū)別)
1叫惊、ArrayList繼承于 AbstractList, LinkedList繼承于 AbstractSequentialList做修;
2霍狰、ArrayList基于數(shù)組抡草, LinkedList基于雙向鏈表,對(duì)于隨機(jī)訪問蔗坯, ArrayList比較占優(yōu)勢(shì)康震,LinkedList插入、刪除元素比較快宾濒,如果只要調(diào)整指針的指向那么時(shí)間復(fù)雜度是O(1),但是如果針對(duì)特定位置需要遍歷時(shí)腿短,時(shí)間復(fù)雜度是O(n),也就是LinkedList在隨機(jī)訪問元素的話比較慢绘梦;
3橘忱、LinkedList沒有實(shí)現(xiàn)自己的 Iterator,但是有 ListIterator和 DescendingIterator卸奉;
4钝诚、LinkedList需要更多的內(nèi)存,因?yàn)?ArrayList的每個(gè)索引的位置是實(shí)際的數(shù)據(jù)榄棵,而 LinkedList中的每個(gè)節(jié)點(diǎn)中存儲(chǔ)的是實(shí)際的數(shù)據(jù)和前后節(jié)點(diǎn)的位置凝颇;
5、ArrayList 和 LinkedList都是非同步的集合秉继。
6祈噪、和ArrayList一樣,LinkedList也是非線程安全的尚辑,只有在單線程下才可以使用辑鲤。為了防止非同步訪問,可以采用如下方式創(chuàng)建LinkedList:List list= Collections.synchronizedList(new LinkedList());
7杠茬、LinkedList基于雙向鏈表實(shí)現(xiàn)月褥,元素都可以為null。
七:推薦相關(guān)文章
http://www.cnblogs.com/tstd/p/5046819.html瓢喉、
https://segmentfault.com/a/1190000003509201