Java程式設計中常用的集合詳解 對你非常有用
Java中的集合概述
集合是一個容器,用來存放引用型別的資料,在java.util包下。
Java中的集合主要有3種類型:
List介面:
是一個有序集合,可以放重複的資料。
Set介面:
是一個無序集合,不允許放重複的資料。
Map介面:
是一個無序集合,集合中包含一個鍵物件,一個值物件,鍵物件不允許重複,值物件可以重複。
Java中Collection集合的繼承實現關係
Java中的Collection集合繼承關係:
相信這裡有很多學習java的朋友,小編整理了一份java方面的學習資料,
有想要學習java的可以加一下我的學習群的喲,60833,4068,歡迎愛學習java的你們!
Iterable介面
此介面只有一個方法宣告,此方法返回一個Iterator介面型別的例項。
Iterable介面宣告:
Iterator介面:
此介面有三個方法申明。主要用於對集合的迭代。
Iterator介面宣告:
Collection介面:
Collection介面很多方法宣告。
Collection介面:
List介面:
有序並且元素可以重複,對於List集合,儲存的順序不會改變。儲存在List集合中的元素可以重複,如一個String型別的物件可以儲存多次。
Set介面:
無順序並且元素不可重複,相同的物件只能儲存一次並且儲存的順序和讀取的順序不一定相同。
SortedSet介面:
無序並且不可重複,另外主要的特點是自動根據大小或字典順序排序。
ArrayList類:
Arraylist實現了List介面中的方法,同時ArrayList也繼承了List介面的特點,存取原素有順序並且元素可以重複。另外,需要注意的地方是ArrayList底層的實現是陣列,所以對於ArrayList集合來說適合查詢而不善於頻繁的動態增刪元素。因為陣列根據陣列的下標就可以提取相應的元素而不需要從頭遍歷整個陣列中所有的元素,陣列的這個特點使ArrayList更適合大量查詢的場景。而陣列的另一個特點是增刪資料代價高,當增加或刪除原素時,部分原素要進行移動,使效能下降。
LinkedList類:
LinkedList類實現了List介面中的方法,同時LinkedList也繼承了List介面的特點,存取原素有順序並且元素可以重複。另外需要注意的是LinkedList集合底層的實現是雙向連結串列,所以LinkedList更適合頻繁的增刪操作,但是查詢效率不如ArrayList集合,兩種集合型別是互補的。
Vector類:
Vector集合底層的實現與ArrayList集合相同,都是陣列的方式實現。另外,Vector集合是執行緒安全的,對於多執行緒集合操作適合Vector集合,如果ArrayList集合被多執行緒操作,同時增刪集合中的資料那麼讀取的資料可能是錯誤的。但是Vector效率較低,使用較少。通常執行緒安全是在程式碼中控制,而不需要利用Vector中多執行緒安全而降低程式的效率。
HashSet類:
HashSet實現了Set介面並實現了Set介面中的方法,HashSet也同時繼承了Set介面的特點,集合中的元素無序並且不可以重複,儲存順序和讀取順序不一定相同。HashSet底層的實現是雜湊表資料結構。
TreeSet類:
TreeSet直接實現的介面是SortedSet介面,和HashSet相同是元素不可重複,但是集合中的元素會自動進行排序,根據大小或字典順序。
ArrayList
ArrayList類是一個特殊的陣列。它來自於System.Collections名稱空間;通過新增和刪除元素,就可以動態改變陣列的長度。ArrayList實現了List介面,是順序容器,即元素存放的資料與放進去的順序相同,允許放入null元素,底層通過陣列實現。除該類未實現同步外,其餘跟Vector大致相同。每個ArrayList都有一個容量(capacity),表示底層陣列的實際大小,容器記憶體儲元素的個數不能多於當前容量。當向容器中新增元素時,如果容量不足,容器會自動增大底層陣列的大小。
繼承關係
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
size(), isEmpty(), get(), set()方法均能在常數時間內完成,add()方法的時間開銷跟插入位置有關,addAll()方法的時間開銷跟新增元素的個數成正比。其餘方法大都是線性時間。
為追求效率,ArrayList沒有實現同步(synchronized),如果需要多個執行緒併發訪問,使用者可以手動同步,也可使用Vector替代。
方法剖析
set()
既然底層是一個數組ArrayList的set()方法也就變得非常簡單,直接對陣列的指定位置賦值即可。
public E set(int index, E element) {
rangeCheck(index);//下標越界檢查
E oldValue = elementData(index);
elementData[index] = element;//賦值到指定位置,複製的僅僅是引用
return oldValue;
}
get()
get()方法同樣很簡單,唯一要注意的是由於底層陣列是Object[],得到元素後需要進行型別轉換。
public E get(int index) {
rangeCheck(index);
return (E) elementData[index];//注意型別轉換
}
add()
對應的方法是add(E e)和add(int index, E e)。這兩個方法都是向容器中新增新元素,這可能會導致capacity不足,因此在新增元素之前,都需要進行剩餘空間檢查,如果需要則自動擴容。擴容操作最終是通過grow()方法完成的。
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//原來的3倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);//擴充套件空間並複製
}
由於Java GC自動管理了記憶體,這裡也就不需要考慮源陣列釋放的問題。
空間的問題解決後,插入過程就顯得非常簡單。
add(int index, E e)需要先對元素進行移動,然後完成插入操作,也就意味著該方法有著線性的時間複雜度。
addAll()
addAll()方法能夠一次新增多個元素,根據位置不同也有兩個把本,一個是在末尾新增的addAll(Collection<? extends E> c)方法,一個是從指定位置開始插入的addAll(int index, Collection<? extends E> c)方法。跟add()方法類似,在插入之前也需要進行空間檢查,如果需要則自動擴容;如果從指定位置插入,也會存在移動元素的情況。
addAll()的時間複雜度不僅跟插入元素的多少有關,也跟插入的位置相關。
remove()
remove()方法也有兩個版本,一個是remove(int index)刪除指定位置的元素,另一個是remove(Object o)刪除第一個滿足o.equals(elementData[index])的元素。刪除操作是add()操作的逆過程,需要將刪除點之後的元素向前移動一個位置。需要注意的是為了讓GC起作用,必須顯式的為最後一個位置賦null值。
歡迎關注胖胖程式設計師,視覺化學習java,每天更新文章,讓Java學習更加簡單。