LinkedList 新增元素原始碼解析

jeremylai發表於2021-09-14

jdk版本:1.8

LinkedList新增元素有兩個方法:add(E e)和add(int index,E e)。

add(E e)

/**
*  Appends the specified element to the end of this list.
*  在列表最後新增指定元素
*/
public boolean add(E e) {
  linkLast(e);
  return true;
}

add(E e)是直接在隊尾新增元素。再看一下linkLast(E e)方法,原始碼如下。

void linkLast(E e) {
  //找到連結串列的最後一個節點,賦值給l,
  final Node<E> l = last;
  //建立節點,這個節點的上一個節點就是上面last節點
  final Node<E> newNode = new Node<>(l, e, null);
 //將新的節點賦值給最後節點
  last = newNode;
  if (l == null)
      //如果沒有最後節點,表示新增連結串列為空,賦值給首節點。
      first = newNode;
  else
      //有最後一個節點,將最後節點的next指標指向新建的節點
      l.next = newNode;
  size++;
  modCount++;
}
  1. LinkedList會記錄連結串列的最後一個節點last,
  2. 首先建立新的節點,新節點的pre就是佇列的最後一個節點last,新節點的next為null,
  3. 如果last為空表示這個連結串列為空,新節點就是首節點first。
  4. 如果last不為空表示連結串列不為空,將last節點的next指標指向新節點。

add(int index,E e)

add(int index,E e)是根據元素插入到指定位置上,index表示連結串列的位置

/**
 * Inserts the specified element at the specified position in this list.
 * 插入指定的元素到列表指定位置上
 */
public void add(int index, E element) {
    //檢查位置是否越界
    checkPositionIndex(index);
    //如果插入的下標等於連結串列的大小,直接就是新增到隊尾,
    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}
  1. 首先檢查index是否超過連結串列大小,即index 是否會大於連結串列的size。
  2. 如果index等於連結串列的大小,新增元素就是在連結串列隊尾新增元素,和add(E e) 操作一致。
  3. 如果不等會size大小就呼叫linkBefore方法,首先在該方法的第二個引數使用了node方法。node方法原始碼如下:
 Node<E> node(int index) {
  //size >>1 表示size右移,表示size的一半
  //如果index小於size一半,從首節點往後遍歷
  if (index < (size >> 1)) {
      Node<E> x = first;
      for (int i = 0; i < index; i++)
          x = x.next;
      return x;
  //如果index大於size一半,從最後一個節點往前遍歷
  } else {
      Node<E> x = last;
      for (int i = size - 1; i > index; i--)
          x = x.prev;
      return x;
  }
}

node方法通過連結串列的位置找到連結串列的元素。這裡用到了一個size的右移運算,size>>1表示size/2。首先判斷index是在連結串列的前一半還是後一半,因為linkedList是雙連結串列,可以往前和往後進行遍歷。如果在前半部分就從首節點往後遍歷,如果在後半部分就從最後一個節點往前遍歷,這樣最多遍歷size的一半,避免遍歷整個連結串列。
找到index對應的元素後執行linkedBefore方法

/**
 * Inserts element e before non-null Node succ.
*   往succ節點前面插入元素
 */
void linkBefore(E e, Node<E> succ) {
    //succ的前一個節點
    final Node<E> pred = succ.prev;
   //建立新節點,新節點的pre就是succ的pre節點,新節點的next就是succ節點
    final Node<E> newNode = new Node<>(pred, e, succ);
   //succ.pre指向新節點
    succ.prev = newNode;
    //如果succ前節點為空,表示succ就是首節點,新節點即為首節點
    if (pred == null)
        first = newNode;
    else
    //succ的上一節點的next指向新節點
        pred.next = newNode;
    size++;
    modCount++;
}

  1. 新建節點,節點pre就是succ的pre,節點的next就是succ。
  2. 將succ.pre指向新節點。
  3. succ的pre為null,succ即為首節點,將first賦值給首節點
  4. succ的pre不為空,則把succ的上一節點的next指向新節點

總結

  1. LinkedList,只有兩種新增方式,一種是在列表最後新增(linkLast),一種是在列表某個元素前面新增 (linkBefore)。
  2. LinkLast,首先建立一個新節點,節點pre指向最後一個節點,最後一個節點的next指向新節點。
  3. LinkBefore,首先根據index下標獲取到元素的位置,新建新節點,新節點的pre就是元素的pre,新節點的next就是該元素。元素的pre的next指向新元素。

為何Linked要用雙連結串列而不是單連結串列

LinkedList為何是雙連結串列,連結串列主要缺點是查詢速度很慢,新增或者刪除都要找到要新增和刪除的節點,而使用雙連結串列,每次遍歷迴圈前,都會判斷一下索引是在連結串列的前半部分還是後半部分。如果是前半部分的話,從首部遍歷到中間。如果是後半部分,從尾部遍歷到中間。

相關文章