java集合原始碼解析:collection
JAVA集合的框架圖:
從圖中可以看出集合分為collection 和 map 兩大類, 其中collection內部主要以陣列或者連結串列的形式存放一系列集合物件,map則是以系列鍵值對的集合
collection主要包含list 和 set 兩個部分,是list和set 高度抽象出來的介面,主要包含add() remove() contains()等集合基本方法,還包含一個iterator() 方法,
依賴iterator介面,可以用來對集合進行遍歷
AbstractCollection是一個抽象類, 實現了collection中的部分方法,比如:
其中用到的siez()方法,以及iterator()等方法,則由具體的子類實現,比如list或者setpublic 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; }
AbstractList 和 AbstractSet 則分別實現了List 和 Set介面,並都繼承自AbstractCollection
List中我們用的最多的是ArrayList和LinkedList , ArrayList內部使用陣列實現,而LinkedList則使用連結串列的方式實現,
先看看ArrayList的部分原始碼:
如果初始化時不指定大小,則預設初始化一個無內容的陣列,指定了大小,則根據指定大小進行初始化, 其中size用來表示陣列實際長度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); } }
我們先看看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