Java 基礎(四)集合源碼解析 List

List 接口

前面咱們學習了Iterator、Collection,爲集合的學習打下了基礎,如今咱們來學習集合的第一大致系 List。數組

List 是一個接口,定義了一組元素是有序的、可重複的集合。安全

List 繼承自 Collection,較之 Collection,List 還添加了如下操做方法bash

  • 位置相關: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,不用佔用額外空間維護表結構

從成員變量,咱們能夠得知ui

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

如今咱們知道了 ArrayList 其實就是基於數組的實現。所以,增刪改查操做就變得很容易理解了。this

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

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

  • 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?

相關文章
相關標籤/搜索