文章首發(fā)csdn博客地址:https://blog.csdn.net/u013277209?viewmode=contents
前言
本篇作為吃透java集合系列第四篇,主要來(lái)了解一下LinkedList疚鲤,通過(guò)本篇你將了解到如下:
1、LinkedList的List特性
2薄嫡、LinkedList的Queue特性
一:LinkedList
LinkedList類是List接口的實(shí)現(xiàn)類封断,它是一個(gè)集合掀序,可以根據(jù)索引來(lái)隨機(jī)的訪問(wèn)集合中的元素,還實(shí)現(xiàn)了Deque接口烦绳,它還是一個(gè)隊(duì)列卿捎,可以當(dāng)成雙端隊(duì)列來(lái)使用。
雖然LinkedList是一個(gè)List集合径密,但是它的實(shí)現(xiàn)方式和ArrayList是完全不同的午阵,ArrayList的底層是通過(guò)一個(gè)動(dòng)態(tài)的Object[]數(shù)組實(shí)現(xiàn)的,而LinkedList的底層是通過(guò)雙向鏈表來(lái)實(shí)現(xiàn)的享扔,因此它的隨機(jī)訪問(wèn)速度是比較差的底桂,但是它的刪除,插入操作很快惧眠。
- LinkedList是基于雙向循環(huán)鏈表實(shí)現(xiàn)的籽懦,除了可以當(dāng)作鏈表操作外,它還可以當(dāng)作棧氛魁、隊(duì)列和雙端隊(duì)列來(lái)使用暮顺。
- LinkedList同樣是非線程安全的,只在單線程下適合使用秀存。
- LinkedList允許null插入捶码。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- 實(shí)現(xiàn)List接口,具有List集合的特性或链。
- 實(shí)現(xiàn)Deque接口惫恼,具有隊(duì)列的特性。
- 實(shí)現(xiàn)Cloneable接口澳盐,可以通過(guò)clone來(lái)實(shí)現(xiàn)淺拷貝
- 實(shí)現(xiàn)Serializable接口祈纯,可以序列化令宿,通過(guò)writeObject和readObject自定義序列化。
LinkedList的底層結(jié)構(gòu)如下圖所示
F表示頭結(jié)點(diǎn)引用腕窥,L表示尾結(jié)點(diǎn)引用粒没,鏈表的每個(gè)結(jié)點(diǎn)都有三個(gè)元素,分別是前繼結(jié)點(diǎn)引用(P)簇爆,結(jié)點(diǎn)元素的值(E)革娄,后繼結(jié)點(diǎn)的引用(N)。結(jié)點(diǎn)由內(nèi)部類Node表示冕碟,我們看看它的內(nèi)部結(jié)構(gòu)。
private static class Node<E> {
E item;//當(dāng)前元素
Node<E> next;//下一個(gè)節(jié)點(diǎn)
Node<E> prev;//上一個(gè)節(jié)點(diǎn)
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
Node這個(gè)內(nèi)部類其實(shí)很簡(jiǎn)單匆浙,只有三個(gè)成員變量和一個(gè)構(gòu)造器安寺,item表示結(jié)點(diǎn)的值,next為下一個(gè)結(jié)點(diǎn)的引用首尼,prev為上一個(gè)結(jié)點(diǎn)的引用挑庶,通過(guò)構(gòu)造器傳入這三個(gè)值。接下來(lái)再看看LinkedList的成員變量和構(gòu)造器软能。
/**
* 集合元素個(gè)數(shù)
*/
transient int size = 0;
/**
* 頭結(jié)點(diǎn)
*/
transient Node<E> first;
/**
* 尾節(jié)點(diǎn)
*/
transient Node<E> last;
/**
* 無(wú)參構(gòu)造器
*/
public LinkedList() {
}
/**
* 傳入外部集合的構(gòu)造器
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
LinkedList持有頭結(jié)點(diǎn)的引用和尾結(jié)點(diǎn)的引用迎捺,它有兩個(gè)構(gòu)造器,一個(gè)是無(wú)參構(gòu)造器查排,一個(gè)是傳入外部集合的構(gòu)造器凳枝。與ArrayList不同的是LinkedList沒(méi)有指定初始大小的構(gòu)造器。
二:LinkedList的List特性
//增
/**
* 將指定的元素追加到此列表的末尾
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* 在此列表中的指定位置插入指定的元素跋核。
*/
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
/**
* 將指定集合中的所有元素追加到此列表的末尾岖瑰。
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
/**
* 將指定集合中的所有元素插入到此列表中,從指定的位置開(kāi)始砂代。
*/
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;
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;
}
/**
* 頭插入
*/
public void addFirst(E e) {
linkFirst(e);
}
/**
* 尾插入
*/
public void addLast(E e) {
linkLast(e);
}
//刪
/**
* 從列表中刪除指定元素的第一個(gè)出現(xiàn)(如果存在)蹋订。
*/
public boolean remove(Object o) {
if (o == 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;
}
/**
* 刪除該列表中指定位置的元素。
*/
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
/**
* 從此列表中刪除并返回第一個(gè)元素刻伊。
*/
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
/**
* 從此列表中刪除并返回最后一個(gè)元素露戒。
*/
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
//改
/**
* 用指定的元素替換此列表中指定位置的元素。
*/
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
//查
/**
* 返回此列表中指定位置的元素捶箱。
*/
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
LinkedList的添加元素的方法主要是調(diào)用linkLast和linkBefore兩個(gè)方法智什,linkLast方法是在鏈表后面鏈接一個(gè)元素,linkBefore方法是在鏈表中間插入一個(gè)元素讼呢。LinkedList的刪除方法通過(guò)調(diào)用unlink方法將某個(gè)元素從鏈表中移除撩鹿。下面我們看看鏈表的插入和刪除操作的核心代碼。
/**
* 返回指定位置的節(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;
}
}
/**
* 連接到第一個(gè)元素
*/
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
/**
* 鏈接到最后一個(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++;
}
/**
* 在succ前插入元素e
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
/**
* 去掉頭結(jié)點(diǎn)
*/
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
/**
* 去掉尾結(jié)點(diǎn)
*/
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
/**
* 去掉指定的節(jié)點(diǎn)
*/
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
三:LinkedList的Queue特性
通過(guò)對(duì)雙向鏈表的操作還可以實(shí)現(xiàn)單項(xiàng)隊(duì)列悦屏,雙向隊(duì)列和棧的功能节沦。
單向隊(duì)列操作:
//獲取頭結(jié)點(diǎn)
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//獲取頭結(jié)點(diǎn)
public E element() {
return getFirst();
}
//彈出頭結(jié)點(diǎn)
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//移除頭結(jié)點(diǎn)
public E remove() {
return removeFirst();
}
//在隊(duì)列尾部添加結(jié)點(diǎn)
public boolean offer(E e) {
return add(e);
}
雙向隊(duì)列操作:
//在頭部添加
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
//在尾部添加
public boolean offerLast(E e) {
addLast(e);
return true;
}
//獲取頭結(jié)點(diǎn)
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//獲取尾結(jié)點(diǎn)
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
棧操作:
//入棧
public void push(E e) {
addFirst(e);
}
//出棧
public E pop() {
return removeFirst();
}
不管是單向隊(duì)列還是雙向隊(duì)列還是棧键思,其實(shí)都是對(duì)鏈表的頭結(jié)點(diǎn)和尾結(jié)點(diǎn)進(jìn)行操作,它們的實(shí)現(xiàn)都是基于addFirst()甫贯,addLast()吼鳞,removeFirst(),removeLast()這四個(gè)方法叫搁。
- LinkedList是基于雙向鏈表實(shí)現(xiàn)的赔桌,不論是增刪改查方法還是隊(duì)列和棧的實(shí)現(xiàn),都可通過(guò)操作結(jié)點(diǎn)實(shí)現(xiàn)
- LinkedList無(wú)需提前指定容量渴逻,因?yàn)榛阪湵聿僮骷驳常系娜萘侩S著元素的加入自動(dòng)增加
- LinkedList刪除元素后集合占用的內(nèi)存自動(dòng)縮小,無(wú)需像ArrayList一樣調(diào)用trimToSize()方法
- LinkedList的所有方法沒(méi)有進(jìn)行同步惨奕,因此它也不是線程安全的雪位,應(yīng)該避免在多線程環(huán)境下使用