主要內容:
- LinkedList繼承關系、關鍵屬性、構造函數
- 數據結構
- 插入、刪除、修改以及查找元素
- 與ArrayList比較
LinkedList概述
介紹LinkedList,就會想到ArrayList,兩者都實現了List接口。但ArrayList底層是基于數組實現,隨機訪問優于LinkedList;而LinkedList底層基于鏈表實現,插入、刪除操作效率優于LinkedList。
- 基于鏈表實現。
- 插入、刪除操作快速,但隨機訪問元素緩慢。
- 非線程安全,創建線程安全的LinkedList可以使用
Collections.synchronizedList
。
ArrayList map = Collections.synchronizedList(new LinkedList());
源碼分析
繼承關系
LinkedList繼承關系.png
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- 繼承AbstractSequentialList抽象類,序列訪問數據,類方法是通過迭代器實現的
- 實現List接口,有序的隊列
- 實現Deque接口,可以當作雙端隊列使用
- 實現java.io.Serialization接口,支持序列化
- 實現Cloneable接口,支持對象克隆,淺復制
關鍵屬性
//實際存儲的元素個數
transient int size = 0;
//頭節點
transient Node<E> first;
//尾部節點
transient Node<E> last;
節點類
LinkedList基于雙向循環鏈表實現,每個節點元素都有指向前一個、后一個元素的引用,以及當前存儲的元素值。
private static class Node<E> {
E item;
Node<E> next;//后一個元素的引用
Node<E> prev;//前一個元素的引用
Node(Node<E> prev, E element, Node<E> next) {//構造一個節點
this.item = element;
this.next = next;
this.prev = prev;
}
}
構造函數
//構造空的LinkedList
public LinkedList() {
}
//構造指定集合的LinkedList,并且按集合迭代的元素順序排序
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
插入
插入元素
插入單個元素的方法主要有boolean add(E e)
和void add(int index, E element)
。
-
boolean add(E e)
:表示將指定元素插入到LinkedList尾部,插入鏈表尾部調用的方法是linkLast(E e)
。
public boolean add(E e) {
linkLast(e);//插入尾部
return true;
}
void linkLast(E e) {//插入LinkedList的尾部
final Node<E> l = last;
//新建一個節點,前一個節點是原來的尾節點,下一個節點為null
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;//將新建的節點作為尾節點
if (l == null)
first = newNode;//空LinkedList
else
l.next = newNode;
size++;
modCount++;
}
-
void add(int index, E element)
:表示將指定元素插入到index位置處。如果插入的位置在鏈表尾部,直接插入到鏈表尾部;如果插入的位置不在鏈表尾部,要調用的方法是linkBefore(E e, Node<E> succ)
,還需要調用node(int index)
獲得某位置上的元素。
public void add(int index, E element) {
checkPositionIndex(index);//判斷位置的范圍
if (index == size)
linkLast(element);//插入尾部
else
linkBefore(element, node(index));//將元素插入到index位置節點之前
}
void linkBefore(E e, Node<E> succ) {//將元素插入到succ節點前
// assert succ != null;
final Node<E> pred = succ.prev;//獲取succ前一個節點
final Node<E> newNode = new Node<>(pred, e, succ);//創建新節點,插入到pred和succ之間
succ.prev = newNode;//改變succ的前一個節點的引用
if (pred == null)
first = newNode;//LinkedList只有一個元素
else
pred.next = newNode;
size++;
modCount++;
}
Node<E> node(int index) {//獲取index位置上的節點
// 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用來表示插入節點的前一個節點,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;
}
刪除
刪除元素方法主要有E remove(int index)
、boolean remove(Object o)
和實現Deque接口中的E remove()
。
-
E remove(int index)
表示刪除索引位置上的元素,boolean remove(Object o)
表示刪除首次出現的指定元素,都會調用unlink(Node<E> x)
。
//刪除索引位置上的元素
public E remove(int index) {
checkElementIndex(index);//防止越界
return unlink(node(index));
}
//移除首次出現的指定元素
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) {//刪除節點x
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;//要刪除節點的后一個節點
final Node<E> prev = x.prev;//要刪除節點的前一個節點
if (prev == null) {//x為頭節點
first = next;
} else {
prev.next = next;
x.prev = null;//釋放x的前節點的引用
}
if (next == null) {//x為尾節點
last = prev;
} else {
next.prev = prev;
x.next = null;//釋放x的后節點的引用
}
x.item = null;
size--;
modCount++;
return element;
}
-
E remove()
:刪除頭節點。
//刪除頭節點
public E remove() {
return removeFirst();
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)//頭節點為null,拋出NoSuchElementException
throw new NoSuchElementException();
return unlinkFirst(f);
}
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; //釋放頭節點的后一個節點引用
first = next;
if (next == null)//刪除頭節點后LinkedList為空
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
修改
將數組中指定位置上的元素替換掉,返回以前位于該位置上的元素。
public E set(int index, E element) {//替代index位置上的節點值
checkElementIndex(index);//防止越界
Node<E> x = node(index);//獲取index位置處的節點
E oldVal = x.item;
x.item = element;//替代
return oldVal;
}
查找
返回指定位置上的元素。
public E get(int index) {//獲取index位置處的節點值
checkElementIndex(index);//防止越界
return node(index).item;
}
與ArrayList比較
LinkedList比ArrayList插入、刪除更快,但這個說法不是很準確。
- ArrayList插入、刪除操作時,查找到該位置的元素迅速,但是復制元素消耗時間
- LinkedList插入、刪除操作時,查找需要操作的Entry元素需要消耗一定時間,但改變該節點前后引用地址快速