溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

LinkedList的深入了解

發(fā)布時間:2020-07-10 00:41:49 來源:網(wǎng)絡(luò) 閱讀:113 作者:ckllf 欄目:編程語言

  什么是LinkedList?

  LinkedList是一種雙向鏈表。那什么是雙向鏈表?根據(jù)雙向鏈表的特點就是會有頭節(jié)點和尾節(jié)點,并且節(jié)點之間是通過前驅(qū)指針和后繼指針來維護關(guān)系的,而不是像數(shù)組那樣通過上下標(biāo)來維護節(jié)點之間的關(guān)系的。也就是說雙向鏈表即可以從頭到尾遍歷,也可以從尾到頭遍歷

  LinkedList與ArrayList的區(qū)別

  大致區(qū)別如下:

  1、ArrayList是基于動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu),LinkedList是基于鏈表的數(shù)據(jù)結(jié)構(gòu)

  2、對于隨機訪問 get() 和 set() 操作,ArrayList優(yōu)于LinkedList,因為LinkedList沒有繼承RandomAccess,而且LinkedList要移動指針

  3、對于add(新增)操作和remove(刪除)操作,LinkedList比較占優(yōu)勢,因為ArrayList要移動數(shù)據(jù)

  LinkedList繼承了哪些類與接口?

  public class LinkedList

  extends AbstractSequentialList

  implements List, Deque, Cloneable, java.io.Serializable{

  }

  LinkedList 類繼承了 AbstractSequentialList 類,并實現(xiàn)了 List、Deque、Cloneable、Serializable

  LinkedList中主要的成員變量

  // 初始化鏈表的長度為0

  transient int size = 0;

  // 指向頭節(jié)點的變量

  transient Node first;

  // 指向尾節(jié)點的變量

  transient Node last;

  LinkedList的構(gòu)造方法

  LinkedList()

  // 創(chuàng)建一個空的構(gòu)造方法

  public LinkedList() {

  }

  LinkedList(Collection c)

  // 創(chuàng)建一個指定Collection對象作為參數(shù)的構(gòu)造函數(shù),元素的順內(nèi)由這個對象迭代返回的順序決定

  public LinkedList(Collection c) {

  this();

  addAll(c);

  }

  addAll(Collection c)方法

  // 將集合中的所有元素從指定的位置開始插入這個列表

  public boolean addAll(Collection c) {

  return addAll(size, c);

  }

  addAll(int index, Collection c)方法

  public boolean addAll(int index, Collection c) {

  // 判斷下標(biāo)元素是否在鏈表的長度范圍之內(nèi)

  checkPositionIndex(index);

  // 將集合轉(zhuǎn)換為Object數(shù)組

  Object[] a = c.toArray();

  // 計算Object數(shù)組的長度

  int numNew = a.length;

  // 如果Object數(shù)組長度為0

  if (numNew == 0)

  // 返回布爾值false

  return false;

  // pred節(jié)點為succ節(jié)點的前驅(qū)

  Node pred, succ;

  // 如果下標(biāo)等于鏈表長度的時候

  if (index == size) {

  // succ節(jié)點指向為null

  succ = null;

  // pred節(jié)點指向尾節(jié)點

  pred = last;

  } else {

  // 否則如果下標(biāo)不等于鏈表長度的話,那么succ節(jié)點就是node()方法通過下標(biāo)索引獲得

  succ = node(index);

  // 獲得鏈表中的對應(yīng)的節(jié)點對象

  pred = succ.prev;

  }

  // 遍歷要添加內(nèi)容的數(shù)組

  for (Object o : a) {

  @SuppressWarnings("unchecked") E e = (E) o;

  // 創(chuàng)建新的節(jié)點,將遍歷的值包裝成Node節(jié)點

  Node newNode = new Node<>(pred, e, null);

  // 如果前驅(qū)節(jié)點為null

  if (pred == null)

  // 那么新的節(jié)點就是頭節(jié)點

  first = newNode;

  else

  // 否則pred指向新創(chuàng)建的節(jié)點

  pred.next = newNode;

  // pred節(jié)點往后移動一位

  pred = newNode;

  }

  // 因為pred節(jié)點是succ節(jié)點的前驅(qū)節(jié)點,反過來也就是說succ是pred的后繼節(jié)點

  // 所以如果succ為null,表示pred為尾節(jié)點

  if (succ == null) {

  last = pred;

  } else {

  /**

  * 否則如果succ不是尾節(jié)點,那么只要保證pred節(jié)點是succ節(jié)點的前驅(qū)節(jié)點、

  succ節(jié)點是pred的后繼節(jié)點這種雙向鏈表的關(guān)系就可以了

  */

  pred.next = succ;

  succ.prev = pred;

  }

  // 元素個數(shù)增加

  size += numNew;

  modCount++;

  return true;

  }

  checkPositionIndex(int index)方法

  private void checkPositionIndex(int index) {

  if (!isPositionIndex(index))

  // 拋出 IndexOutOfBoundsException 異常

  throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

  }

  isPositionIndex(int index)方法

  private boolean isPositionIndex(int index) {

  // 這個這么簡單就不解釋了吧

  return index >= 0 && index <= size;

  }

  Node節(jié)點

  private static class Node {

  E item; // 節(jié)點的值

  Node next; // 指向前一個節(jié)點

  Node prev; // 指向后一個節(jié)點

  // 初始化

  Node(Node prev, E element, Node next) {

  this.item = element;

  this.next = next;

  this.prev = prev;

  }

  }

  LinkedList的常用方法

  add(E e)

  // 使用雙向鏈表的尾插法

  public boolean add(E e) {

  // 將元素插入到鏈表尾部

  linkLast(e);

  return true;

  }

  linkLast(E e)

  // 插入的節(jié)點為尾節(jié)點

  void linkLast(E e) {

  // 創(chuàng)建一個節(jié)點并初始化為尾節(jié)點

  final Node l = last;

  // 初始化新的節(jié)點,前驅(qū)的節(jié)點為l,后繼節(jié)點為null

  final Node newNode = new Node<>(l, e, null);

  // 因為是在鏈表的尾部插入節(jié)點,所以新的節(jié)點作為尾節(jié)點(這個不難理解)

  last = newNode;

  // l節(jié)點作為newNode節(jié)點的前驅(qū)節(jié)點,如果l為空,則說明newNode前驅(qū)節(jié)點為空

  if (l == null)

  // 在雙向鏈表中,前驅(qū)節(jié)點為空,那么該節(jié)點為頭節(jié)點

  first = newNode;

  else

  // 否則 l 的下一個節(jié)點指向該節(jié)點

  l.next = newNode;

  // 鏈表長度+1

  size++;

  modCount++;

  }

  add(int index, E element)

  // 指定位置插入元素

  public void add(int index, E element) {

  // 判斷下標(biāo)索引是否在鏈表長度范圍內(nèi)(上述講到過)

  checkPositionIndex(index);

  // 如果下標(biāo)索引等于鏈表長度

  if (index == size)

  // 則采用尾插法(剛剛講到過)

  linkLast(element);

  else

  // 否則采用頭插法

  linkBefore(element, node(index));

  }

  linkBefore()

  // 插入的節(jié)點為頭節(jié)點,在節(jié)點succ之前插入元素e

  void linkBefore(E e, Node succ) {

  // assert succ != null;

  // succ節(jié)點的前驅(qū)節(jié)點為pred

  final Node pred = succ.prev;鄭州哪家醫(yī)院看婦科好 http://www.120zzkd.com/

  // 初始化新的前驅(qū)節(jié)點為pred,后繼節(jié)點為succ(簡單地說就是在pred和succ節(jié)點之間插入元素)

  final Node newNode = new Node<>(pred, e, succ);

  // 后繼節(jié)點為succ,succ的前驅(qū)節(jié)點為newNode

  succ.prev = newNode;

  // 如果pred為null

  if (pred == null)

  // 則newNode為頭節(jié)點

  first = newNode;

  else

  // 否則pred的下一個節(jié)點指向newNode

  pred.next = newNode;

  // 鏈表長度+1

  size++;

  modCount++;

  }

  remove(Object o)

  // 刪除元素

  public boolean remove(Object o) {

  /** 通過雙向鏈表的前后關(guān)系,遍歷雙向鏈表。

  * 判斷是否存在元素和要刪除的元素相同。

  * 如果遍歷到了,那么就刪除元素,并且返回true

  */

  if (o == null) {

  for (Node x = first; x != null; x = x.next) {

  if (x.item == null) {

  unlink(x);

  return true;

  }

  }

  } else {

  for (Node x = first; x != null; x = x.next) {

  if (o.equals(x.item)) {

  unlink(x);

  return true;

  }

  }

  }

  // 如果沒遍歷到,那么就返回false

  return false;

  }

  unlink()

  // 刪除非空節(jié)點

  E unlink(Node x) {

  // assert x != null;

  final E element = x.item;

  // 獲取該元素的后繼節(jié)點

  final Node next = x.next;

  // 獲取該元素的前驅(qū)節(jié)點

  final Node prev = x.prev;

  // 如果前驅(qū)節(jié)點pred為null

  if (prev == null) {

  // 表示當(dāng)前要刪除的節(jié)點為頭節(jié)點,讓pred的后繼節(jié)點作為頭節(jié)點

  first = next;

  } else {

  // 否則直接使用雙向鏈表刪除節(jié)點

  prev.next = next;

  // 將刪除節(jié)點x的前驅(qū)節(jié)點設(shè)置為null

  x.prev = null;

  }

  // 如果后繼節(jié)點為null

  if (next == null) {

  // 表示當(dāng)前刪除的節(jié)點為尾節(jié)點,將前驅(qū)節(jié)點作為尾節(jié)點

  last = prev;

  } else {

  // 否則如果后繼節(jié)點不為null,使用雙向鏈表刪除節(jié)點

  next.prev = prev;

  // 將刪除節(jié)點x的后繼節(jié)點設(shè)置為null

  x.next = null;

  }

  // 將刪除節(jié)點的值設(shè)置為null,方便垃圾回收

  x.item = null;

  // 鏈表長度-1

  size--;

  modCount++;

  return element;

  }

  get(int index)

  // 獲取下標(biāo)元素

  public E get(int index) {

  // 判斷下標(biāo)是否在鏈表長度范圍內(nèi)(上述講到過)

  checkElementIndex(index);

  // 獲取下標(biāo)節(jié)點,返回節(jié)點的值

  return node(index).item;

  }

  node(int index)

  // 獲取下標(biāo)節(jié)點

  Node node(int index) {

  // assert isElementIndex(index);

  // 判斷下標(biāo)索引是靠近頭節(jié)點還是尾節(jié)點

  if (index < (size >> 1)) {

  // 獲取頭節(jié)點

  Node x = first;

  // 遍歷index的節(jié)點

  for (int i = 0; i < index; i++)

  x = x.next;

  return x;

  } else {

  // 獲取尾節(jié)點

  Node x = last;

  // 遍歷index的節(jié)點

  for (int i = size - 1; i > index; i--)

  x = x.prev;

  return x;

  }

  }

  總結(jié)

  1、LinkedList 添加的元素在取的時候與添加的時候的順序一致。因為向雙向鏈表的尾部添加元素,然后按照頭節(jié)點順序遍歷獲取,所以一致

  2、LinkedList 允許添加重復(fù)元素

  3、LinkedList 不是線程安全的集合

  4、LinkedList 允許添加 null 元素

  5、add 方法插入元素是在雙向鏈表的尾部插入

  6、get 方法遍歷雙向鏈表,先判斷下標(biāo)靠近頭節(jié)點還是尾節(jié)點,這樣會減少多余的循環(huán)


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI