【Java源碼解析第一篇】List

首先先來看一下繼承結構圖:
在這裏插入圖片描述
如圖所示爲List的繼承關係結構圖。由圖可知,Java的Collection集合類採用了設計模式裏的模板模式——接口定義方法,抽象類實現基礎通用功能,具體實現類繼承抽象類並進行功能的自定義實現。

Collection接口

Collection接口定義了Java集合類的通用方法,如元素的添加、刪除等方法,其方法列表可見下圖。由於接口只是方法定義,其重要之處在於設計理念,這裏不做過多介紹。
在這裏插入圖片描述

List接口

List接口方法列表圖如圖所示,如圖可知,List接口相對Collection接口而言多了許多方法,主要包含一些針對列表數據結構的方法重載以及元素的增加、刪除、索引方法,以及一個特殊的雙向迭代器ListIterator。
在這裏插入圖片描述

AbstractCollection

在這裏插入圖片描述
AbstractCollection對Collection接口的一些方法提供默認實現:

  • 對size()和iterator()保留抽象,由子類實現
  • 通過判斷size()是否爲0判斷是否爲empty
  • contains()的實現爲獲取迭代器對集合元素進行迭代查找
  • toArray()使用系統數組拷貝形式實現
  • add()默認拋出異常,這是由於不同的數據結構實現列表添加元素方式不同,且存在列表不支持結構性改變操作的需求
  • remove()使用迭代器遍歷查找目標並刪除目標
  • containsAll()遍歷調用contains()
  • addAll()遍歷調用add()
  • clear()遍歷調用remove()

AbstractList

在這裏插入圖片描述
如圖可知,AbstractList抽象類主要對List接口的一些基本功能進行實現,其保留了大部分繼承自AbstractCollection的默認實現,僅對三個方法做了重寫操作。

  • add()、set()、remove()全部拋出不支持異常,交給子類複寫實現,若不復寫則默認不支持結構性改變操作
  • get()仍然交給不同的實現類根據自己的數據結構自行實現
  • 對於indexOf()、clear()……等方法其實現原理均與AbstractCollection類似,不再複述
  • AbstractList返回的迭代器是內部類,對於迭代器的實現,主要依賴兩個索引指針cursor和lastRet;前者是下一個元素的索引,後者是當前元素的索引,next()方法直接get(cursor)並更新lastRet=cursor和cursor++。ListIterator相比較Iterator具備雙向移動的特點,在向前移動時實際上只是將cursor的初始值指向了末尾並做遞減操作。
  • subList()根據隨機訪問屬性創建內部類對象,該內部類List繼承AbstractList。
class SubList<E> extends AbstractList<E> {
    private final AbstractList<E> l;
    private final int offset;
    private int size;

    SubList(AbstractList<E> list, int fromIndex, int toIndex) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > list.size())
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
        l = list;
        offset = fromIndex;
        size = toIndex - fromIndex;
        this.modCount = l.modCount;
    }

如代碼所示,l=list即兩者指向的是同一個列表對象,因此原列表的改變同樣會在子列表中表現出來,同時這個內部類的所有結構性改變方法都會進行結構性次數比對,當發現原列表的結構改變次數與此SubList對象改變次數不一致時,會拋出異常。這是由於結構性改變發生時會導致子列表的offset和size等發生異常。

  • modCount用來記錄結構性改變次數,主要用來告知迭代器,如果發生意外結構性改變迭代器將拋出異常。

ArrayList

  • 底層是數組,使用elementData[]保存數據。
  • 內部提前定義好緩存數組elementData,根據構造時提供的初始化容量來新建一個容量大小的數組對象,如果不指定則指向一個默認容量的空數組。使用其他collection進行構造時,elementData直接指向傳入的集合.toArray(),並將數組轉型爲object[]類型.
  • trimToSize()如果list存放的元素數size小於elementData的length,使用數組複製的方式將元素賦值到length爲size大小的新數組中去。
  • 擴容操作均是通過數組複製方式將元素複製到更大容量的數組中
  • clone返回新對象
  • get讀取數組,set覆蓋元素返回舊值,add末尾添加或中間添加(數組複製),remove根據序號或者值進行刪除,序號直接數組複製,值需要遍歷查找再數組複製
  • clear使用for遍歷置null(不會改變結構)
  • sort使用Arrays.sort
  • foreach() 支持lamed表達式的foreach遍歷

AbstractSequentialList

繼承自AbstractList,提供有序數據存儲的基礎實現

LinkedList

  • 底層爲雙向鏈表,Node數據結構如下:
E item;
Node<E> next;
Node<E> prev;
  • 屬性記錄首節點和尾節點。
  • linkFirst 新建一個前指向爲空,後節點指向原首節點的新節點,然後將原節點的前指針指向這個新節點,如果原首節點爲空,則新節點也標記爲尾節點。其他link方法參考該思路
  • clear遍歷置爲null
  • 查找某個索引的node,根據索引決定從頭還是從尾找,依次遍歷賦值next直到抵達索引位置然後返回。
  • 刪除節點時直接根據當前節點記錄的前後節點指針獲取前後節點,並將前後節點對當前節點的指針進行刪除(置null)修正工作。
  • 對接口Deque實現的方法如poll、push等操作,本質也是對首尾節點的操作,不再複述。
  • get(index)會根據index距離首尾距離進行遍歷,直到index,因此linkedlist用for循環遍歷會帶來極大的性能開銷,應該使用迭代器。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章