LinkedList是基於雙向鏈表的集合,先看以下代碼:
List<Integer> list = new LinkedList<>();
for(int i = 1;i < 6;i++){
list.add(i);
}
transient int size = 0;
/**
* Pointer to first node.
*/
transient Node<E> first;
/**
* Pointer to last node.
*/
transient Node<E> last;
LinkedList的三個數據成員,size是集合內元素的個數,first指向頭節點,last指向尾節點。而集合內的元素是通過內部類node來存儲的,定義如下: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;
}
}
包括三個成員變量,item是存儲的數據元素,next指向後一個Node節點,perv指向前一個Node節點。1、構造方法
只有兩個構造方法,一個無參構造,一個有參
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
它和ArrayList不一樣,沒有指定初始化容量的構造函數。有參構造就是把集合全部加入到鏈表中,
2、add方法
public boolean add(E e) {
linkLast(e);
return true;
}
直接調用linkLast方法,看一下linkLast方法: void linkLast(E e) {
final Node<E> l = last; //把last節點賦值給l
final Node<E> newNode = new Node<>(l, e, null); //根據加入的元素構造新的node節點
last = newNode; //新的node節點設置爲last節點
if (l == null) //last節點不爲空的時候
first = newNode; //即添加第一個節點的時候last爲null,就把新節點設置爲first
else
l.next = newNode; //把l節點(之前的last)的next指向新的node節點
size++; // 數據的size加1
modCount++;
}
add方法就是把新節點設爲last節點,在第三行的構造函數裏面,把新節點的perv指向之前的last節點l,然後把之前last節點l的next指向新節點。最後一行modCount是集合中記錄修改次數的成員變量,和快速失敗機制有關。具體查看Java從入門到放棄(七)集合框架之ArrayList的坑
public void add(int index, E element) {
checkPositionIndex(index); //鏈表越界檢查
if (index == size)
linkLast(element); //添加到last節點方法,如上
else
linkBefore(element, node(index)); 在node(index)節點前面添加新節點
}
node(index)方法就是獲取鏈表中index位置的node:
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) { //如果在鏈表的前半部分,就從first向後遍歷
Node<E> x = first;
for (int i = 0; i < index; i++) 循環index次
x = x.next;
return x;
} else {
Node<E> x = last; //如果在鏈表的後半部分,就從last向前遍歷
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
可以看到內部做了優化,並不是直接遍歷到index位置,而是進行了判斷,當index在鏈表的前半部分就從first向後遍歷,如果在後半部分就從last向前遍歷。
linkBefore方法:
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev; //獲取succ的perv節點
final Node<E> newNode = new Node<>(pred, e, succ); //構建新節點,新節點的next指向succ,新節點的perv指向succ的perv
succ.prev = newNode; //succ的perv節點指向新節點
if (pred == null)
first = newNode; //如果perv節點爲null,說明succ是first節點,所以把first指向新節點
else
pred.next = newNode; //succ的perv節點的next節點指向新節點
size++;
modCount++;
}
執行如下代碼後結果如圖:
list.add(2,6)
3、remove方法
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;
}
就是遍歷鏈表,找到對應的節點,然後用unlink方法刪除,看一下unlink方法:E unlink(Node<E> x) {
// assert x != null;
final E element = x.item; //node節點存儲的數據
final Node<E> next = x.next; //後一個節點
final Node<E> prev = x.prev; //前一個節點
if (prev == null) {
first = next; //如果x的perv節點爲null,說明x節點就是first,所以要把x的next節點設爲first節點
} else {
prev.next = next; //x節點的perv節點的next節點設置爲x節點的next節點
x.prev = null; //x的perv節點置爲null,便於gc
}
if (next == null) {
last = prev; //如果x的next節點爲null,說明x節點就是last節點,所以要把x的perv節點設爲last節點
} else {
next.prev = prev; //x節點的next節點的perv節點設置爲x節點的perv節點
x.next = null; //x的next節點置爲null,便於gc
}
x.item = null; //x的item置爲null,便於gc
size--;
modCount++;
return element;
}
如下代碼執行後:list.remove(3);
remove(index)方法:
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
方法都是調用了之前的一些方法,鏈表越界檢查,然後根據node(index)知道對應的節點,使用unlink方法刪除,這兩個方法上面都有說明。