主要內(nèi)容:
- LinkedList繼承關(guān)系、關(guān)鍵屬性蔓彩、構(gòu)造函數(shù)
- 數(shù)據(jù)結(jié)構(gòu)
- 插入野舶、刪除妓笙、修改以及查找元素
- 與ArrayList比較
LinkedList概述
介紹LinkedList,就會(huì)想到ArrayList幅狮,兩者都實(shí)現(xiàn)了List接口募强。但ArrayList底層是基于數(shù)組實(shí)現(xiàn),隨機(jī)訪問優(yōu)于LinkedList崇摄;而LinkedList底層基于鏈表實(shí)現(xiàn)擎值,插入、刪除操作效率優(yōu)于LinkedList配猫。
- 基于鏈表實(shí)現(xiàn)幅恋。
- 插入、刪除操作快速泵肄,但隨機(jī)訪問元素緩慢捆交。
- 非線程安全,創(chuàng)建線程安全的LinkedList可以使用
Collections.synchronizedList
腐巢。
ArrayList map = Collections.synchronizedList(new LinkedList());
源碼分析
繼承關(guān)系
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- 繼承AbstractSequentialList抽象類品追,序列訪問數(shù)據(jù),類方法是通過迭代器實(shí)現(xiàn)的
- 實(shí)現(xiàn)List接口冯丙,有序的隊(duì)列
- 實(shí)現(xiàn)Deque接口肉瓦,可以當(dāng)作雙端隊(duì)列使用
- 實(shí)現(xiàn)java.io.Serialization接口,支持序列化
- 實(shí)現(xiàn)Cloneable接口胃惜,支持對象克隆泞莉,淺復(fù)制
關(guān)鍵屬性
//實(shí)際存儲(chǔ)的元素個(gè)數(shù)
transient int size = 0;
//頭節(jié)點(diǎn)
transient Node<E> first;
//尾部節(jié)點(diǎn)
transient Node<E> last;
節(jié)點(diǎn)類
LinkedList基于雙向循環(huán)鏈表實(shí)現(xiàn),每個(gè)節(jié)點(diǎn)元素都有指向前一個(gè)船殉、后一個(gè)元素的引用鲫趁,以及當(dāng)前存儲(chǔ)的元素值。
private static class Node<E> {
E item;
Node<E> next;//后一個(gè)元素的引用
Node<E> prev;//前一個(gè)元素的引用
Node(Node<E> prev, E element, Node<E> next) {//構(gòu)造一個(gè)節(jié)點(diǎn)
this.item = element;
this.next = next;
this.prev = prev;
}
}
構(gòu)造函數(shù)
//構(gòu)造空的LinkedList
public LinkedList() {
}
//構(gòu)造指定集合的LinkedList利虫,并且按集合迭代的元素順序排序
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
插入
插入元素
插入單個(gè)元素的方法主要有boolean add(E e)
和void add(int index, E element)
挨厚。
-
boolean add(E e)
:表示將指定元素插入到LinkedList尾部堡僻,插入鏈表尾部調(diào)用的方法是linkLast(E e)
。
public boolean add(E e) {
linkLast(e);//插入尾部
return true;
}
void linkLast(E e) {//插入LinkedList的尾部
final Node<E> l = last;
//新建一個(gè)節(jié)點(diǎn)疫剃,前一個(gè)節(jié)點(diǎn)是原來的尾節(jié)點(diǎn)钉疫,下一個(gè)節(jié)點(diǎn)為null
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;//將新建的節(jié)點(diǎn)作為尾節(jié)點(diǎn)
if (l == null)
first = newNode;//空LinkedList
else
l.next = newNode;
size++;
modCount++;
}
-
void add(int index, E element)
:表示將指定元素插入到index位置處。如果插入的位置在鏈表尾部巢价,直接插入到鏈表尾部牲阁;如果插入的位置不在鏈表尾部,要調(diào)用的方法是linkBefore(E e, Node<E> succ)
蹄溉,還需要調(diào)用node(int index)
獲得某位置上的元素咨油。
public void add(int index, E element) {
checkPositionIndex(index);//判斷位置的范圍
if (index == size)
linkLast(element);//插入尾部
else
linkBefore(element, node(index));//將元素插入到index位置節(jié)點(diǎn)之前
}
void linkBefore(E e, Node<E> succ) {//將元素插入到succ節(jié)點(diǎn)前
// assert succ != null;
final Node<E> pred = succ.prev;//獲取succ前一個(gè)節(jié)點(diǎn)
final Node<E> newNode = new Node<>(pred, e, succ);//創(chuàng)建新節(jié)點(diǎn),插入到pred和succ之間
succ.prev = newNode;//改變succ的前一個(gè)節(jié)點(diǎn)的引用
if (pred == null)
first = newNode;//LinkedList只有一個(gè)元素
else
pred.next = newNode;
size++;
modCount++;
}
Node<E> node(int index) {//獲取index位置上的節(jié)點(diǎn)
// assert isElementIndex(index);
if (index < (size >> 1)) {//index小于size的一半柒爵,從前往后找
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {//index大于size的一半役电,從后往前找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
插入集合元素
插入集合的方法主要有boolean addAll(Collection<? extends E> c)
和boolean addAll(int index, Collection<? extends E> c)
。
//按照集合迭代器的順序棉胀,將集合的元素插入到鏈表尾部
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
//從指定位置上開始法瑟,插入集合的元素
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//防止越界
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;//pred用來表示插入節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn),succ表示后一個(gè)節(jié)點(diǎn)
if (index == size) {//元素是插入到鏈表尾部
succ = null;
pred = last;
} else {//元素插入到鏈表中
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
刪除
刪除元素方法主要有E remove(int index)
唁奢、boolean remove(Object o)
和實(shí)現(xiàn)Deque接口中的E remove()
霎挟。
-
E remove(int index)
表示刪除索引位置上的元素,boolean remove(Object o)
表示刪除首次出現(xiàn)的指定元素麻掸,都會(huì)調(diào)用unlink(Node<E> x)
酥夭。
//刪除索引位置上的元素
public E remove(int index) {
checkElementIndex(index);//防止越界
return unlink(node(index));
}
//移除首次出現(xiàn)的指定元素
public boolean remove(Object o) {
if (o == null) {//元素為null
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
E unlink(Node<E> x) {//刪除節(jié)點(diǎn)x
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;//要?jiǎng)h除節(jié)點(diǎn)的后一個(gè)節(jié)點(diǎn)
final Node<E> prev = x.prev;//要?jiǎng)h除節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)
if (prev == null) {//x為頭節(jié)點(diǎn)
first = next;
} else {
prev.next = next;
x.prev = null;//釋放x的前節(jié)點(diǎn)的引用
}
if (next == null) {//x為尾節(jié)點(diǎn)
last = prev;
} else {
next.prev = prev;
x.next = null;//釋放x的后節(jié)點(diǎn)的引用
}
x.item = null;
size--;
modCount++;
return element;
}
-
E remove()
:刪除頭節(jié)點(diǎn)。
//刪除頭節(jié)點(diǎn)
public E remove() {
return removeFirst();
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)//頭節(jié)點(diǎn)為null脊奋,拋出NoSuchElementException
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {//刪除頭節(jié)點(diǎn)熬北,并返回頭節(jié)點(diǎn)的值
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;//頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)
f.item = null;//釋放頭節(jié)點(diǎn)的值
f.next = null; //釋放頭節(jié)點(diǎn)的后一個(gè)節(jié)點(diǎn)引用
first = next;
if (next == null)//刪除頭節(jié)點(diǎn)后LinkedList為空
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
修改
將數(shù)組中指定位置上的元素替換掉,返回以前位于該位置上的元素诚隙。
public E set(int index, E element) {//替代index位置上的節(jié)點(diǎn)值
checkElementIndex(index);//防止越界
Node<E> x = node(index);//獲取index位置處的節(jié)點(diǎn)
E oldVal = x.item;
x.item = element;//替代
return oldVal;
}
查找
返回指定位置上的元素讶隐。
public E get(int index) {//獲取index位置處的節(jié)點(diǎn)值
checkElementIndex(index);//防止越界
return node(index).item;
}
與ArrayList比較
LinkedList比ArrayList插入、刪除更快久又,但這個(gè)說法不是很準(zhǔn)確巫延。
- ArrayList插入、刪除操作時(shí)地消,查找到該位置的元素迅速炉峰,但是復(fù)制元素消耗時(shí)間
- LinkedList插入、刪除操作時(shí)脉执,查找需要操作的Entry元素需要消耗一定時(shí)間疼阔,但改變該節(jié)點(diǎn)前后引用地址快速