JDK容器學習之LinkedList:底層存儲&讀寫邏輯

LinkedList的底層結構及讀寫邏輯

鏈表容器,一般適用於頻繁的新增刪除,遍歷的方式訪問數據元素的場景java

LinkedList 底層數據結構爲鏈表,非線程安全,本片博文則簡單分析下增刪改對鏈表的操做姿式,以及LinkedList的迭代實現node

I. 數據結構

雙向鏈表存儲,內部保存表頭和表尾對象,size用來記錄List的長度安全

transient int size = 0;

/**
 * Pointer to first node.
 * Invariant: (first == null && last == null) ||
 *            (first.prev == null && first.item != null)
 */
transient Node<E> first;

/**
 * Pointer to last node.
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
transient Node<E> last;


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支持正向和反向的迭代數據結構

II. 新增,刪除,讀取邏輯

1. 讀取邏輯

獲取鏈表中某個索引處的值,只能經過遍從來實現查找this

由於是雙向鏈表,若以根據索引index和實際的長度size,能夠斷定是從前日後找快仍是從後往遷找快,查看源碼,判斷具體實現是否有作區分線程

public E get(int index) {
    // 越界判斷
    checkElementIndex(index);
    return node(index).item;
}

Node<E> node(int 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;
    }
}

實現方式和咱們的預期相同,二分判斷,若index在前半段,則從鏈表頭開始查找;不然從鏈表尾進行查找code

2. 新增元素

添加元素,區分爲在鏈表尾添加和在鏈表中間進行添加,算是基本數據結構中鏈表操做方法對象

public void add(int index, E element) {
    // 越界判斷
    checkPositionIndex(index);

    if (index == size)
        linkLast(element);
    else {
      // 先獲取到index索引對應的節點,而後在該節點以前插入新的元素
        linkBefore(element, node(index));
    }
}

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++;
}


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++;
}

新增元素示意圖索引

輸入圖片說明

3. 刪除元素

鏈表中元素刪除比較簡單,修改先後節點的next,first引用便可圖片

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;
}

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;
}

說明

  • 刪除一個不存在的元素,返回false
  • LinkedList中能夠存null

III. 小結

  1. LinkedList底層結構爲雙向鏈表
  2. 能夠塞入null到鏈表中
  3. 有序,非線程安全
  4. 適用場景
    • 不要求線程安全
    • 插入刪除頻繁
    • 經過遍歷方式訪問元素(或較少的根據索引來查詢元素)

掃描關注,java分享

相關文章
相關標籤/搜索