Java基礎——集合源碼解析 List List 介面

来源:http://www.cnblogs.com/aishangJava/archive/2017/09/26/7599477.html
-Advertisement-
Play Games

今天我們來學習集合的第一大體系 List。 List 是一個介面,定義了一組元素是有序的、可重覆的集合。 List 繼承自 Collection,較之 Collection,List 還添加了以下操作方法 位置相關:List 的元素是有序的,因此有get(index)、set(index,objec ...


今天我們來學習集合的第一大體系 List。

List 是一個介面,定義了一組元素是有序的、可重覆的集合。

List 繼承自 Collection,較之 Collection,List 還添加了以下操作方法

  • 位置相關:List 的元素是有序的,因此有get(index)、set(index,object)、add(index,object)、remove(index) 方法。
  • 搜索:indexOf(),lastIndexOf();
  • 迭代:使用 Iterator 的功能板迭代器
  • 範圍性操作:使用 subList 方法對 list 進行任意範圍操作。

List的抽象實現類 AbstractList

AbstractList 繼承自 AbstractCollection 類,實現了 List 介面。整個類的設計類似於AbstractCollection,實現了大多數方法,抽象了對於需要根據數據操作的方法。

List 的實現類

ArrayList

ArrayList 是我們最常用的一個類,它具有如下特點:

  • 容量不固定,可以動態擴容
  • 有序(基於數組的實現,當然有序~~)
  • 元素可以為 null
  • 效率高
    • 查找操作的時間複雜度是 O(1)
    • 增刪操作的時間複雜度是 O(n)
    • 其他操作基本也都是 O(n)
  • 占用空間少,相比 LinkedList,不用占用額外空間維護表結構

從成員變數,我們可以得知

  • Object[] elementData:數據結構---數組
  • 兩個預設空數組,僅在構造方法中使用,不關心
  • DEFAULT_CAPACITY: 數組初始容量為10
  • size:當前元素個數
  • MAX_ARRAY_SIZE:數組最大容量

現在我們知道了 ArrayList 其實就是基於數組的實現。因此,增刪改查操作就變得很容易理解了。

  • get(index)直接獲取數組的底 index 個元素
  • set(index,object)直接修改數組的第 index 個元素的引用
  • add(index,object)添加一個元素到index,這裡會牽涉到數組的擴容,擴容我們後面再單獨看

這裡的操作很簡單,比如說含有8個元素的數組array,要在第五個位置插入一個元素x,則將第[5,8)角標的元素分別往後移動一位變成[6,9),此時角標為5的位置空出來,使 array[5] = x 即可。

  • remove(object)刪除一個元素,刪除的過程同添加元素。

現在我們來看看擴容機制,假設我們現在有一個集合 list,裡面正好含有10個元素,此時,我們調用 add(object)方法添加一個元素,看看是怎樣執行擴容操作的。

public boolean add(E var1) {
    //查看是數組長度是否夠
    this.ensureCapacityInternal(this.size + 1);
    this.elementData[this.size++] = var1;
    return true;
}

private void ensureCapacityInternal(int var1) {
    //檢查是否是預設長度為0的數組,如果是則長度設為10
    if(this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        var1 = Math.max(10, var1);
    }
    this.ensureExplicitCapacity(var1);
}

private void ensureExplicitCapacity(int var1) {
    ++this.modCount;
    //當前需要的長度大於數組長度,執行擴容操作
    if(var1 - this.elementData.length > 0) {
        this.grow(var1);
    }
}

private void grow(int var1) {
    int var2 = this.elementData.length;
    //var3 = var2*1.5;擴容1.5倍
    int var3 = var2 + (var2 >> 1);
    if(var3 - var1 < 0) {
        var3 = var1;
    }
    //新容量超出最大值
    if(var3 - 2147483639 > 0) {
        var3 = hugeCapacity(var1);
    }
    //重新創建了一個1.5倍容量的數組賦值給elementData
    this.elementData = Arrays.copyOf(this.elementData, var3);
}

從上面我們可以看到,修改某個角標的值或者查找某個角標的值時,我們可以直接調用數組的操作,效率很高。但是添加和刪除則是要操作整個數組的移動,效率稍低。

這裡我們也可以看到,其實 ArrayList 就是一個針對數組操作的封裝類。

LinkedList

剛剛我們看了 ArrayList,ArrayList 的增刪操作效率相對較低。因此,Java 給我們設計了另外一種增刪效率比較高的集合 LinkedList。

LinkedList 繼承自 AbstractSequentialList。

AbstractSequentialList 又繼承自AbstractList,並且基於 iterator 實現了預設增刪改查操作。

再回過頭來看 LinkedList,LinkedList 還實現了Deque(雙向隊列)介面,雙向隊列後面我們會單獨去學習,這裡不再做過多的贅述。

再來看看成員變數~~

  • size 鏈表元素個數
  • first 第一個元素
  • last 最後一個元素

特點?和 ArrayList 的優缺點互補。

鏈表的實現,鏈表的實現很簡單,就是最基本的鏈表數據結構。理解鏈表數據結構可以跳過這裡了。

我舉個例子吧,現在要讓 a、b、c、d 四個同學按順序投籃。有兩種方法,第一種是 abcd 排成一個隊伍,依次去投籃;但是體育課上讓所有的同學等著投籃很浪費時間,因此有了第二種辦法:依次告訴 a‘你投了藍之後叫 b 來投籃’,告訴 b‘你投了藍之後叫 c 來投籃’以此類推。
這樣,就形成了一個簡單的單向列表,abcd 按照順序依次去投籃。此時,x 同學由於身體不舒服需要提前投籃回教室休息,則老師只需要告訴 a 同學投完籃之後不用叫 b 同學了,改叫 x 同學,x 同學投完籃之後叫 b 同學即可。
不多說了,新手聽不懂,老手用不上。不懂鏈表的同學好好去學學數據結構吧。

後面的增刪改查操作就只是基礎的遍歷鏈表操作,就不去一一去讀源碼了,鏈表操作記不太清楚的同學可以去看一下 LinkedList 的源碼。

Vector

在學習了 ArrayList 之後,Vector 這個類我想用”線程安全的 ArrayList“可以一句話概括了。

Vector 和 ArrayList 一樣都繼承自 AbstractList,為什麼說”Vector 是線程安全的 ArrayList“,本來還準備列個表讓大家對比一下成員變數以及主要操作方法的實現。but,除了 Vector 的方法上多了個 synchronized 外,代碼都是一樣的,比較個毛。因此,如果需要考慮線程安全,直接使用 Vector 即可,但是因為所有方法都加了synchronized ,效率相對會比較低,如果沒有線程安全的需求,那就使用 ArrayList 唄。

最後,還是說一下Vector 和 ArrayList的區別吧,反正也沒什麼卵用,大家看看就好

  • Vector 出生早,JDK1.0的時候出生的,ArrayList 是1.2才出生
  • Vector 是線程安全的,ArrayList 不是。(這是最大的特點了)
  • Vector 預設擴容2倍,ArrayList 是1.5倍。這個~~有意義嗎?
  • Vector 多了一種迭代器 Enumeration。好吧,反正我沒用過。
Enumeration

為了找到 Enumeration 這種迭代器有什麼特點,我去翻了一下 Vector 的代碼,找到了一個這樣的方法和這樣的介面,你們感受一下。

public Enumeration<E> elements() {
    return new Enumeration() {
        int count = 0;
        public boolean hasMoreElements() {
            return this.count < Vector.this.elementCount;
        }
        public E nextElement() {
            Vector var1 = Vector.this;
            synchronized(Vector.this) {//區別在這裡
                if(this.count < Vector.this.elementCount) {
                    return Vector.this.elementData(this.count++);
                }
            }
            throw new NoSuchElementException("Vector Enumeration");
        }
    };
}

public interface Enumeration<E> {
    boolean hasMoreElements();

    E nextElement();
}

mmp,這個 Enumeration 和Iterator 介面除了名字不一樣,還有什麼區別?然後仔細看了一遍,在elements()方法裡面的匿名內部了裡面找到了nextElement()方法裡面有個同步代碼塊。好吧, Enumeration 大概是線程安全的Iterator?

Stack

Stack 繼承自Vector,也是一個線程安全的集合。

Stack 也是基於數組實現的。

Stack 實現的是棧結構集合

什麼是棧結構?

數據結構中,棧也是一種線性的數據結構,遵守 LIFO(後進先出)的操作順序,這裡用一張很污的圖,保證你們看了之後能熟記棧結構特征。

小時候肯定都玩過羽毛球吧,羽毛球不經打,要經常換球,於是我買了一盒羽毛球,如下圖,就是一個羽毛球盒子,最先放進去的羽毛球(棧底的),要最後才能取出來。

Stack 的 代碼實現

類結構圖如下,代碼量也不多,一共才30幾行

public class Stack<E> extends Vector<E> {
    private static final long serialVersionUID = 1224463164541339165L;

    public Stack() {
    }

    //入棧,添加一個元素到數組的最後一個
    public E push(E var1) {
        this.addElement(var1);
        return var1;
    }
    //出棧,刪除數組最後一個元素並返回
    public synchronized E pop() {
        int var2 = this.size();
        Object var1 = this.peek();
        this.removeElementAt(var2 - 1);
        return var1;
    }
    //獲取最後一個元素,不刪除
    public synchronized E peek() {
        int var1 = this.size();
        if(var1 == 0) {
            throw new EmptyStackException();
        } else {
            return this.elementAt(var1 - 1);
        }
    }

    public boolean empty() {
        return this.size() == 0;
    }
    獲取棧中的 位置。
    public synchronized int search(Object var1) {
        int var2 = this.lastIndexOf(var1);
        return var2 >= 0?this.size() - var2:-1;
    }
}

整個類的實現非常簡單,就是繼承 Vector,然後添加了 peek、pop、push、search 等方法,然後然添加刪除都在最末尾的元素做操作即可。

思考:怎樣用鏈表的結構快速實現 LinkedListStack?

Java學習交流QQ群:589809992  禁止閑聊,非喜勿進!


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

-Advertisement-
Play Games
更多相關文章
  • 在以前的博文中我們介紹了Slick,它是一種FRM(Functional Relation Mapper)。有別於ORM,FRM的特點是函數式的語法可以支持靈活的對象組合(Query Composition)實現大規模的代碼重覆利用,但同時這些特點又影響了編程人員群體對FRM的接受程度,阻礙了FRM ...
  • jps:JVM Process StatusTool,顯示指定系統內所有的HotSpot虛擬機進程 jstat:JVM Statistics Monitoring Tool,用於手機HotSpot虛擬機各方面的運行數據 jinfo: Configuration Info for Java 顯示虛擬機 ...
  • 在瞭解了 "AQS獨占鎖模式" 以後,接下來再來看看共用鎖的實現原理。 原文地址:http://www.jianshu.com/p/1161d33fc1d0 搞清楚AQS獨占鎖的實現原理之後,再看共用鎖的實現原理就會輕鬆很多。兩種鎖模式之間很多通用的地方本文只會簡單說明一下,就不在贅述了,具體細節可 ...
  • 本章內容繞不開一個名詞:RTTI(Run-time Type Identification) 運行時期的類型識別 知乎上有人推斷作者是從C++中引入這個概念的,反正也無所謂,理解並能串聯本章知識才是最重要的 本章的內容其實都是為類型信息服務的,主要內容有 一.Class對象 問題: 1.Class對 ...
  • 1. #!/usr/bin/python 和#!/usr/bin/env python 含義 大部分python文件的頭部都會寫上 #!/usr/bin/python 或者 #!/usr/bin/env ,這個語句主要和運行模式有關, 如果我們用普通運行模式例如(linux) : python *. ...
  • 首先,先看個例子吧 上面的代碼,是典型的懶漢式單例模式,在單線程的情況下,完全是沒有問題的,但是在多線程的環境下,就很難保證對象的單例了。那應該如何去保證在多線程的環境下的單例呢?相信學過了,學過多線程的基本知識的都知道在可能會產生併發的地方,加上同步就好,即synchronized(同步方法或者同 ...
  • 模擬購物車程式,判斷用戶薪資是否是0 如果是0就需要輸入薪資,並記錄到文件內。 可以預先存個字典格式的字元串,然後去讀取文件的時候讀到的是字字元串然後再去用eval去轉換成字典。 當我們覆蓋寫到文件的時候就會發現首行會有空格,當我們再去讀取eval的時候就會報錯,那怎麼樣可以解決這個問題呢! ...
  • 參考教程:http://www.tensorfly.cn/tfdoc/tutorials/mnist_beginners.html 數據下載地址:http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/mnist_download.ht ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...