1. 程式人生 > >java集合原始碼解析:collection

java集合原始碼解析:collection

JAVA集合的框架圖:

從圖中可以看出集合分為collection 和 map 兩大類, 其中collection內部主要以陣列或者連結串列的形式存放一系列集合物件,map則是以系列鍵值對的集合

collection主要包含list 和 set 兩個部分,是list和set 高度抽象出來的介面,主要包含add() remove() contains()等集合基本方法,還包含一個iterator() 方法,

依賴iterator介面,可以用來對集合進行遍歷

AbstractCollection是一個抽象類, 實現了collection中的部分方法,比如:

    public boolean isEmpty() {
	return size() == 0;
    }
    public boolean contains(Object o) {
	Iterator<E> e = iterator();
	if (o==null) {
	    while (e.hasNext())
		if (e.next()==null)
		    return true;
	} else {
	    while (e.hasNext())
		if (o.equals(e.next()))
		    return true;
	}
	return false;
    }
其中用到的siez()方法,以及iterator()等方法,則由具體的子類實現,比如list或者set

AbstractList 和 AbstractSet 則分別實現了List 和 Set介面,並都繼承自AbstractCollection

List中我們用的最多的是ArrayList和LinkedList , ArrayList內部使用陣列實現,而LinkedList則使用連結串列的方式實現, 

先看看ArrayList的部分原始碼:

private static final int DEFAULT_CAPACITY = 10;
transient Object[] elementData;
private int size;

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
如果初始化時不指定大小,則預設初始化一個無內容的陣列,指定了大小,則根據指定大小進行初始化, 其中size用來表示陣列實際長度

我們先看看add方法

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

 第一個是直接加在陣列後面,第二個是加在指定下標的位置,arraylist可以根據下標直接定位,所以查詢效率很高,但是在新增和刪除的時候,效率非常低

原因是因為新增和刪除的時候,如果不是從最後一個下標開始操作,那麼需要將指定下標後面所有的元素都進行移動

		List list = new ArrayList(100000);
		//List list = new LinkedList();
		long s1 = System.currentTimeMillis();
		for(int i=0; i<100000; i++) {
			list.add(0,i);
		}
		long s2 = System.currentTimeMillis();
		System.out.println(s2 - s1);
我用這段程式碼進行測試,時間是1123多毫秒,但是我如果把list.add(0,i) 這段程式碼直接改成list.add(i),那麼直需要5毫秒左右

並且隨著陣列容量增加,所需數量會呈指數級增長,所以我們如果需要對list從中間進行新增,刪除操作,那麼儘量使用linkedlist

上面這段程式碼如果把new Arraylist() 直接用new LinkedList()來代替, 兩種add方法需要的時間都在幾毫秒左右


list每次在新增時,必須先對其容量進行計算, ensureCapacityInternal(size + 1) ,如果超出容量,則需進行擴容

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//預設初始容量為10,取所需容量和初始容量的最大值
        }

        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 當所需容量大於陣列初始的長度時,需要進行擴容操作
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    private void grow(int minCapacity) {
        // 擴容一般為原來的1.5倍,如果還不夠,那麼直接取所需容量進行操作,進行擴容操作需要對整個陣列進行移動,效能較差,所以儘可能確定初始容量
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
arraylist的get可以直接定位下標,從陣列中取值,所以效率很高:public E get(int index) {rangeCheck(index);return elementData(index);}

再看看linkedlist的原始碼:

//首先定義了頭尾2個節點,這裡原始碼來自jdk1.8,1.6的原始碼裡面是直接定義了一個head節點並且 頭尾都指向自己,作為一個雙向連結串列
transient Node<E> first;
transient Node<E> last;

//node節點的定義,其實就是一個指定的物件,再加上前後指標
    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
linkedlist的add比較簡單,先判斷 頭節點是否為空,如果頭節點為空,那麼直接將新增的節點作為頭節點,如果不為空,則將頭節點的next指向這個節點,這個節點的next指向原來 頭結點的next,prev也按類似的邏輯進行操作.

linkedlist由於是基於連結串列的,沒有arraylist裡面的擴容操作,新增和刪除都只需要修改幾個指標的指向就行,所以新增和刪除效率高

但是查詢的時候,由於沒有下標,那麼只能通過遍歷來進行比較,所以效率非常低

list中的另一個實現:vector,其基本實現原理和arraylist差不多,但是他在很多方法中加入了synchronized來保證執行緒安全,所以效率低

而且很多情況,這個synchronized其實沒法真正保證執行緒安全(比如size()方法和add(E e)都單獨加了同步,但是我們如果需要先判斷size()然後再add,那麼在多執行緒的情況下,這2個方法執行的間隙,就有可能被其他執行緒修改了資料), 所以vector現在基本被棄用了, 如果需要使用執行緒安全的集合,應該首選java.util.Concurrent下面的相關集合

相關推薦

java集合原始碼解析:collection

JAVA集合的框架圖: 從圖中可以看出集合分為collection 和 map 兩大類, 其中collection內部主要以陣列或者連結串列的形式存放一系列集合物件,map則是以系列鍵值對的集合 collection主要包含list 和 set 兩個部分,是list和

java集合原始碼解析(三)--List

今天給大家帶來有序集合的介面List,我想也應該是大家在工作中用的比較多的 先來看看介面的定義: public interface List<E> extends Collection<E>可以看出介面List直接繼承於介面Collection,並且一樣使用了

java集合原始碼解析(二)--AbstractCollection

今天帶來的是java單列頂層介面的第一個輕量級實現:AbstractCollection 我們直接進入正題,先來看看它的宣告: package java.util; //可以從名字上同樣看到 AbstractCollection 是一個抽象類,所以並不能例項化, //這個類只是作

Java 集合原始碼解析(1):Iterator

Java 提供的 集合類都在 Java.utils 包下,其中包含了很多 List, Set, Map, Queue… 它們的關係如下面這張類圖所示: 可以看到,Java 集合主要分為兩類:Collection 和 Map. 而 Collection 又繼承了 Iter

Java集合原始碼解析:TreeMap

本文概要 二叉查詢樹的用處 二叉查詢樹,以及二叉樹帶來的問題 平衡二叉樹的好處 紅黑樹的定義以及構造 紅黑樹在TreeMap的運用 二叉樹的好處 可能許多人會有疑問,為什麼要使用二叉樹,有那麼多的資料結構,比如陣列、連結串列等 簡單看下陣列和連結串列的優缺點

Java集合原始碼解析:HashMap

本文概要 HashMap概述 HashMap資料結構 HashMap的原始碼解析 HashMap概述 在官方文件中是這樣描述的: Hash table based implementation of the Map interface. This imple

JAVA集合原始碼解析 Hashtable探索(基於JDK1.8)

JDK1.8Hashtable探索 本文的討論分析是基於JDK1.8進行的 依舊是採用前幾篇文章的大綱來進行介紹 1.簡介 Hashtable 採用陣列+單鏈表來實現的,Hashtable 實現了一個雜湊表,它將鍵對映到值。任何非 nu

java集合原始碼解析:map

map裡面用的最多的就是HashMap了, 如果需要對key進行排序的話,會用到 TreeMap 先看看HashMap的原始碼 HashMap內部還是用陣列的方式實現的 transient Node<K,V>[] table; //Node的定義,除了key

Java集合原始碼分析02----Collection集合

目錄   Collection框架綜述 Collection介面 Set介面 List介面 Queue介面 迭代器 -----------參考《Thank In Java》 Collection框架綜述 Collection是一個介面,分為常見的

Java基礎面試題(18)----ArrayList集合原始碼解析

我們對ArrayList集合的原始碼進行解析,只是寫出了增刪改查的方法。 首先我們來看一下ArrayList的資料結構 底層實際上是一個數組,在增加元素的時候,對陣列進行擴容,新增一個元素,容量增加1。 實際儲存的是順序儲存的結構,每個位置的元素都有執行的索

Java集合框架之Collection例項解析

0、集合引入 1)集合的由來? Java是面向物件程式語言,經常需要操作很多物件,必要時需儲存物件(對Java語言而言,儲存的通常是物件的引用,以達到複用或管理等目的),常見容器如陣列和StringBuffer(執行緒安全但效率較低,為了提高效率而

java集合框架02——Collection架構與原始碼分析

Collection是一個介面,它主要的兩個分支是List和Set。如下圖所示:         List和Set都是介面,它們繼承與Collection。List是有序的佇列,可以用重複的元素;而Set是數學概念中的集合,不能有重複的元素。List和Set都有它們各自

【 專欄 】- JUC-Java併發集合原始碼解析

JUC-Java併發集合原始碼解析 JUC包是java.util.concurrent包的簡寫,主要提供高效能的併發工具類,已解決JDK併發方面的弱勢。通過JUC包下的工具類Java開發者可以很容易的開發出高併發高效能的多執行緒安

Java集合原始碼分析之超級介面:Collection

方法定義在閱讀原始碼前,我們可以先自行想象一下,如果我們想封裝下陣列或連結串列以方便操作,我們需要封裝哪些功能呢?比如:統計大小、插入或刪除資料、清空、是否包含某條資料,等等。而Collection就是對這些常用操作進行提取,只是其很全面、很通用。下面我們看看它都提供了哪些方法。//返回集合的長度,如果長度大

Java集合解析

先來 重新 興趣 exp weak hashtable 過程 子類 put Java中的集合類包含的內容很多而且很重要,很多數據的存儲和處理(排序,去重,篩選等)都需要通過集合類來完成。 首先java中集合類主要有兩大分支: (1)Collection (2)Map

Java集合復習Collection

顯式 ext exc collect 尺寸 extend 實現 xtend 可變參數列表 1 import java.util.*; 2 class Snow{} 3 class Powder extends Snow{} 4 class Crusty exten

Java集合復習Collection(2)添加一組元素

ray addall 調整 supported cnblogs rust 數組 dal sta 1 import java.util.*; 2 class Snow{} 3 class Powder extends Snow{} 4 class Crusty ex

List集合原始碼解析原理和用法

 注:以下所用原始碼均基於JDK1.8基礎(特殊說明除外) 先從原始碼入手解析: public interface List<E> extends Collection<E> {} An ordered collection (also know

Java集合原始碼分析03----ArrayList原始碼分析

目錄   簡介 ArrayList介紹(基於jdk1.8) 原始碼分析 案例 簡介 ArrayList位置java.util包下面,是List集合的一種,底層是動態陣列,它的容量能夠動態的增長。ArrayList是非同步的,只能在單執行緒中使用。 Arr

Java集合原始碼分析01----集合框架

在java.util包下面提供了一些集合類,又稱為容器。相比長度固定,存放基本資料的陣列,集合的長度是可變的,並且存放的是物件的引用。Java集合框架學習大致可以分為五個部分:List列表,Set集合,Map對映,迭代器(Iterator,Enumeration),工具類(Arrays,Collec