Java集合框架學習
今天看了一下集合框架,其實指的就是Collection介面和Map介面,其中Collection介面是Iterable介面的子介面。而Map介面上面已經沒有父類介面了。
其中,Iterable介面包含三個方法
一、Collection介面
從Collection介面,就是我們的框架內容了,大致將一下,Collection介面下面有很多的子介面,其他的沒用過,但是有三個我們經常碰見的,就是Set , List,Queue
List:是一種底層是陣列實現的, 元素可重複的有序集合,下面有很多實現,比如ArrayList,LinkedList , Vector等
Set:底層是hash表實現的,不可重複的無序集合,
Queue:用於裝載先進先出原則的資料 ,下面的實現有Dequeue,LinkedList等。
下面來介紹集中常用的實現
1、ArrayList,最常用的集合,底層是陣列,原始碼中的定義如下,和父類List介面一直,是可以存放重複的元素 ,且存放的元素是有序的。
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
ArrayList與普通的陣列有什麼區別呢?
最大的區別就是陣列的長度是固定的,不可變的,一旦超出陣列的長度範圍是會報錯的,而ArrayList底層雖然也是陣列,但是長度卻是可變的,每次增加元素是,也就是add方法,都將會檢查一遍上面定義的這個陣列的長度是否足夠,不夠的話,將新建一個新的陣列,並將老的陣列複製到新的陣列當中。
public boolean add(E e) {
ensureCapacityInternal(size + 1 ); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)//如果下標大於陣列長度,則需要新建一個數組
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
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);
}
第二點就是,集合做到動態擴容犧牲的是自身的效率,因此,陣列的效率是比集合要高的。
第三,集合定義了remove()方法刪除自身元素,而陣列沒有。
第四點,集合存入物件時,拋棄型別資訊,所有物件遮蔽為Object,編譯時不檢查型別,但是執行時會報錯。而陣列一旦定義好了,如果存放不同型別的資料,編譯就會不通過。這個區別總結起來就是,陣列只能存放單一型別,而集合可以存放任意型別,預設就是Object,所有物件。
當然了,集合和陣列也可以轉換,只限於集合存放單一型別
集合–>陣列,List.toArray()
陣列–>集合,Array.asList()
2、LinkedList
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
看定義我們就知道,LinkedList是實現了List和Queue介面的。底層是連結串列實現的。LinkedList通過代價較低的在List中間進行插入和刪除操作,提供了優化的順序訪問。也即是說,相比於ArrayList,LinkedList的插入刪除速度是比ArrayList慢的,而查詢的速度卻更加的快。
原始碼中,unlinkLast()方法和unlinkFirst(),顧名思義,也就是pollLat()he pollFirst()的呼叫,裡面是這麼寫的:
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
/**
* Unlinks non-null last node l.
*/
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
裡面的這句help GC很有意思,意思是讓某個物件=null,就能被GC掉,否則這個物件永遠不會被GC,為什麼呢?翻翻JVM書可以找到答案。
3、Vector
List的另一種實現,使用方法和ArrayList一樣,但是,人家是執行緒安全的,所有操作元素的方法都加了synchronized關鍵字,這也是兩者的最大的區別。
兩者還有一個不同點,就是兩者的擴容方式:
ArrayList:初始容量*3/2+1
Vector:預設擴充兩倍。
4、HashSet
說HashSet之前先看看下面的HashMap,因為HashSet是對HashMap的一層包裝,內部就是一個HashMap,沒什麼區別。先看HashMap。
5、HashMap
Map介面的實現類。以key/value鍵值對存放資料,key值不允許為空,value可以為空,且由於容器可能會對元素進行重新hash,HashMap無法保證元素是有序的。
重新Hash之後,有可能會產生衝突,HashSet處理衝突時候,採用的是衝突連結串列方式。
HashMap的效能問題,一般來說,put和get是可以在常數時間內完成,但是,如果要對HashMap進行迭代,就需要去遍歷整個HashMap和衝突連結串列。因此,如果是迭代次數比較多的場景,HashMap的初始值不應該設定太大。
有兩個引數可以影響HashMap的效能,一個是初始容量(inital capacity)和負載係數(load factor),負載係數指的是自動擴充容量的臨界值。當entry的數量大於(初始容量*和負載係數)時,HashMap將進行擴容,且重新計算Hash。所以,對於插入操作較多的場景,將初始值設定稍大,可以減少重新Hash的次數。
另外,將物件放到HashMap中時,需要注意兩個方法,hashCode()和equals(),當插入物件時,是通過hashCode計算Hash值判斷物件應該插入到什麼位置,當Hash值重複時,就要通過equals()方法去判斷是不是同一個物件。所以說,如果要將自定義的物件放入到HashMap中,需要重寫這兩個方法。
6、HashTable
也是Map的實現類,與HashMap一樣,唯一不同的就是,HashTable是同步的。