基於JDK1.8的LinkedList剖析

来源:https://www.cnblogs.com/wenbochang/archive/2018/03/01/8488604.html
-Advertisement-
Play Games

之前寫了一篇ArrayList,那麼今天就寫一篇他的姊妹篇,LinkedList。 眾所周知,ArrayList底層數據是數組,可以在O(1)的時間內get到數據,但刪除和插入就要O(n)時間複雜度。 所以出現了鏈表,鏈表可以在O(1)的時間內插入,並且不會浪費記憶體,用多少就鏈接多少即可。 我們從以 ...


之前寫了一篇ArrayList,那麼今天就寫一篇他的姊妹篇,LinkedList。

眾所周知,ArrayList底層數據是數組,可以在O(1)的時間內get到數據,但刪除和插入就要O(n)時間複雜度。

所以出現了鏈表,鏈表可以在O(1)的時間內插入,並且不會浪費記憶體,用多少就鏈接多少即可。

我們從以下幾個方面介紹LinkedList

  • Node節點
  • add方法
  • remove方法
  • get方法

(一)Node節點

 1 private static class Node<E> {
 2     E item;
 3     Node<E> next;
 4     Node<E> prev;
 5 
 6     Node(Node<E> prev, E element, Node<E> next) {
 7         this.item = element;
 8         this.next = next;
 9         this.prev = prev;
10     }
11 }

我們可以看出每個結點的組成部分有三個,一個是item數據,一個是prev前驅節點,一個是next後驅節點。

那麼就可以知道LinkedList就是一個雙向鏈表,每個節點既有指向後面的鏈表,也有指向前面的鏈表。如下圖(畫的不好,見諒)

(二)add方法

1 //最基本的add方法,其他方法都是這個方法的變體
2 public boolean add(E e) {
3     linkLast(e);
4     return true;
5 }

直接調用了linkLast方法(也就是說,add方法是預設插入到鏈表的尾端),然後return 一個 true。

 1 void linkLast(E e) {
 2     //將鏈表的last節點給l
 3     final Node<E> l = last;
 4     final Node<E> newNode = new Node<>(l, e, null);
 5     last = newNode;
 6     //如果是第一個節點
 7     if (l == null)
 8         first = newNode;
 9     //直接加入到尾節點的後面去
10     else
11         l.next = newNode;
12     size++;
13     modCount++;
14 }

我們知道add方法是在隊列尾部添加元素,還是很容易的。首先用變數 l 指向最後一個節點,然後創建一個節點將它的prev指向 l ,這樣newnode成為最後一個節點,使用last指向它,接著使 l 的next指向newnode,這種直接添加在隊列尾部的方式還是很好理解的,我們重點看看如何添加在隊列的中間位置。

 1 public void add(int index, E element) {
 2     //檢查插入位置是否合法
 3     checkPositionIndex(index);
 4 
 5     //如果插入到最後,直接調用linkLast方法
 6     if (index == size)
 7         linkLast(element);
 8     //否則調用linkBefore
 9     else
10         linkBefore(element, node(index));
11 }

直接看註釋。在調用linkBefore之前,調用了node(index)確定插入的位置

 1 Node<E> node(int index) {
 2     // assert isElementIndex(index);
 3 
 4     if (index < (size >> 1)) {
 5         Node<E> x = first;
 6         for (int i = 0; i < index; i++)
 7             x = x.next;
 8         return x;
 9     } else {
10         Node<E> x = last;
11         for (int i = size - 1; i > index; i--)
12             x = x.prev;
13         return x;
14     }
15 }

首先判斷在前半部分還是在後半部分,然後一個for迴圈查找。時間複雜度O(n), 沒辦法,鏈表的缺點。

 1 void linkBefore(E e, Node<E> succ) {
 2     // assert succ != null;
 3     final Node<E> pred = succ.prev;
 4     final Node<E> newNode = new Node<>(pred, e, succ);
 5     succ.prev = newNode;
 6     if (pred == null)
 7         first = newNode;
 8     else
 9         pred.next = newNode;
10     size++;
11     modCount++;
12 }

 (三)remove方法

看完了添加,刪除就顯得簡單些,無非分為兩種,從頭部刪除,從中間刪除,從頭部刪除和從尾部添加一樣簡單,從中間刪除就是把此結點的前一個結點的next指向此結點的後一個結點,並把後一個結點的prev指向此節點的前一個結點,就是跳過此結點,最終將此結點null交給GC大人解決。為了篇幅,我們不再贅述。

 (四)get方法

由於LinkedList是鏈表,get方法必須掃描一遍鏈表,效率極低,所以謹慎使用。

1  public E get(int index) {
2      //檢查是否超過鏈表長度或者負數
3     checkElementIndex(index);
4     //node節點我前面分析過了,O(n)複雜度
5     return node(index).item;
6 }

從源代碼中我們可以清晰的看到,所謂的get方法也就是,調用node方法遍歷整個鏈表,只是其中稍微做了點優化,如果index的值小於size/2從頭部遍歷,否則從尾部遍歷。可見效率一樣低下,所以我們以後寫程式的時候,如果遇到數據量不大但是需要經常遍歷查找的時候使用ArrayList而不是LinkedList,如果數據量非常的大,但是不是很經常的查找時使用LinkedList。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天用Eclipse Java EE版寫了幾個java工程項目,然後再寫java EE項目的jsp頁面時,Tomcat出現了這個異常信息: 解決辦法: 在菜單欄Window——>Preferences——>Server——>Runtime Environments,將列表中已經配置的Tomcat給R ...
  • 一、字元串 在Python中,加了引號的字元都被認為是字元串! 單引號、雙引號、多引號的區別? 單引號和 雙引號沒有任何區別,但是某種情況下需要單雙配合 如 msg = " My name is Small Nine ,I ' m 22 years old !’" 多引號的作用? 多引號的作用就是多 ...
  • 首先繼承Thread類,然後重寫Thread類的run()方法。 Thread類的子類的對象調用start()方法,然後虛擬機就會調用該線程的run()方法。 當程式執行到start()方法時,線程啟動,此時有兩條執行路徑,一條是主方法執行main方法,另一條是線程路徑執行線程run()里的代碼,兩 ...
  • 開散列法又叫鏈地址法(開鏈法)。 開散列法:首先對關鍵碼集合用散列函數計算散列地址,具有相同地址的關鍵碼歸於同一子集合,每一個子集合稱為一個桶,各個桶中的元素通過一個單鏈錶鏈接起來,各鏈表的頭結點存儲在哈希表中。 設元素的關鍵碼為37, 25, 14, 36, 49, 68, 57, 11, 散列表 ...
  • 解法: 最開始有三種思路: 最後採用了最後一種思路 github地址:https://github.com/CyanChan/Leetcode-Record ...
  • 近期項目中,用 jenkins 熱部署 web工程時,發現工程中靜態持有的線程(將ScheduledExecutorService定時任務存儲在靜態Map中),導致不定時出現資料庫訪問事務關閉異常,如下:org.springframework.transaction.CannotCreateTran ...
  • word裡面有2張表,需要找到第二張表,並寫入execl中: 代碼如下: 運行後生成文件 roro.xlsx,內容如下: ...
  • Java內部類 一、 含義 在Java編程語言里,程式是由類(class)構建而成的。在一個類的內部也可以聲明類,我們把這 樣的類叫做內部類。 二、 作用 實現了更好的封裝,我們知道,普通類(非內部類)的訪問修飾符不能為private或protected,而內部類可以。當我們將內部類聲明為priva ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...