集合就是一個放數據的容器,準確的說是放數據對象引用的容器;集合類存放的都是對象的引用,而不是對象的本身;集合類型主要有3種:set(集)、list(列表)和map(映射)。
Java1.5引入了泛型,所有的集合接口和實現都大量地使用它。泛型允許我們為集合提供一個可以容納的對象類型,因此,如果你添加其它類型的任何元素,它會在編譯時報錯。這避免了在運行時出現ClassCastException,因為你將會在編譯時得到報錯信息。泛型也使得代碼整潔,我們不需要使用顯式轉換和instanceOf操作符。它也給運行時帶來好處,因為不會產生類型檢查的字節碼指令。
Iterator接口提供遍歷任何Collection的接口。我們可以從一個Collection中使用迭代器方法來獲取迭代器實例。迭代器取代了Java集合框架中的Enumeration。迭代器允許調用者在迭代過程中移除元素。
數組是固定長度的;集合可變長度的。
數組可以存儲基本數據類型,也可以存儲引用數據類型;集合只能存儲引用數據類型。
數組存儲的元素必須是同一個數據類型;集合存儲的對象可以是不同數據類型。
Map接口和Collection接口是所有集合框架的父接口:
Collection接口的子接口包括:Set接口和List接口
Map接口的實現類主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
Set接口的實現類主要有:HashSet、TreeSet、LinkedHashSet等
List接口的實現類主要有:ArrayList、LinkedList、Stack以及Vector等
java.util.Collection 是一個集合接口(集合類的一個頂級接口)。它提供了對集合對象進行基本操作的通用接口方法。Collection接口在Java 類庫中有很多具體的實現。Collection接口的意義是為各種具體的集合提供了最大化的統一操作方式,其直接繼承接口有List與Set。
Collections則是集合類的一個工具類/幫助類,其中提供了一系列靜態方法,用于對集合中元素進行排序、搜索以及線程安全等各種操作。
Set 接口實例存儲的是無序的,不重復的數據。List 接口實例存儲的是有序的,可以重復的元素。都可以存儲null值,但是set不能重復所以最多只能有一個空元素。
Set檢索效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變 <實現類有HashSet,TreeSet>。
List和數組類似,可以動態增長,根據實際存儲的數據的長度自動增長List的長度。查找元素效率高,插入刪除效率低,因為會引起其他元素位置改變 <實現類有ArrayList,LinkedList,Vector> 。
1)Arraylist 底層使用的是Object數組;LinkedList 底層使用的是雙向循環鏈表數據結構;
2)ArrayList 采用數組存儲,所以插入和刪除元素的時間復雜度受元素位置的影響。插入末尾還好,如果是中間,則(add(int index, E element))接近O(n);LinkedList 采用鏈表存儲,所以插入,刪除元素時間復雜度不受元素位置的影響,都是近似 O(1)而數組為近似 O(n)。對于隨機訪問get和set,ArrayList優于LinkedList,因為LinkedList要移動指針。
3)LinkedList 不支持高效的隨機元素訪問,而ArrayList 實現了RandmoAccess 接口,所以有隨機訪問功能。快速隨機訪問就是通過元素的序號快速獲取元素對象(對應于get(int index)方法)。所以ArrayList隨機訪問快,插入慢;LinkedList隨機訪問慢,插入快。
4)ArrayList的空 間浪費主要體現在在list列表的結尾會預留一定的容量空間,而LinkedList的空間花費則體現在它的每一個元素都需要消耗比ArrayList更多的空間(因為要存放直接后繼和直接前驅以及數據)。
ArrayList和Vector在很多時候都很類似。
(1)兩者都是基于索引的,內部由一個數組支持。
(2)兩者維護插入的順序,我們可以根據插入順序來獲取元素。
(3)ArrayList和Vector的迭代器實現都是fail-fast的。
(4)ArrayList和Vector兩者允許null值,也可以使用索引值對元素進行隨機訪問。
以下是ArrayList和Vector的不同點。
(1)Vector是同步的,而ArrayList不是。然而,如果你尋求在迭代的時候對列表進行改變,你應該使用CopyOnWriteArrayList。
(2)ArrayList比Vector快,它因為有同步,不會過載。
(3)ArrayList更加通用,因為我們可以使用Collections工具類輕易地獲取同步列表和只讀列表。
List<String> strList = new ArrayList<>();
//使用for-each循環
for(String obj : strList){
System.out.println(obj);
}
//using iterator
Iterator<String> it = strList.iterator();
while(it.hasNext()){
String obj = it.next();
System.out.println(obj);
}
當把對象加入到HashSet中時,HashSet會先計算對象的hashCode值來判斷對象加入的下標位置,同時也會與其他的對象的hashCode進行比較,如果沒有相同的,就直接插入數據;如果有相同的,就進一步使用equals來進行比較對象是否相同,如果相同,就不會加入成功。
1.使用foreach循環遍歷
Map<String, String> hashMap = new HashMap<String,String>();
hashMap.put("1", "good");
hashMap.put("2", "study");
hashMap.put("3", "day");
hashMap.put("4", "up");
for (Map.Entry<String, String> entry : hashMap.entrySet()) {
? ? System.out.println(entry.getKey()+":"+entry.getValue());
}
2.使用foreach迭代鍵值對
Map<String, String> hashMap = new HashMap<String,String>();
hashMap.put("1", "good");
hashMap.put("2", "study");
hashMap.put("3", "day");
hashMap.put("4", "up");
for (String key : hashMap.keySet()) {
? ? System.out.println(key);
}
for (String value : hashMap.values()) {
? ? System.out.println(value);
}
3.使用迭代器
Map<String, String> hashMap = new HashMap<String,String>();
hashMap.put("1", "good");
hashMap.put("2", "study");
hashMap.put("3", "day");
hashMap.put("4", "up");
Iterator<Map.Entry<String, String>> iterator = hashMap.entrySet().iterator();
while (iterator.hasNext()) {
? ? Map.Entry<String, String> next = iterator.next();
? ? System.out.println(next.getKey()+":"+next.getValue());
}
4.使用lambda表達式
Map<String, String> hashMap = new HashMap<String,String>();
hashMap.put("1", "good");
hashMap.put("2", "study");
hashMap.put("3", "day");
hashMap.put("4", "up");
hashMap.forEach((k,v)-> System.out.println(k+":"+v));
相同點:
都是實現來Map接口(hashTable還實現了Dictionary 抽象類)。
不同點:
1. 歷史原因:Hashtable 是基于陳舊的 Dictionary 類的,HashMap 是 Java 1.2 引進的 Map 接口
的一個實現,HashMap把Hashtable 的contains方法去掉了,改成containsvalue 和containsKey。因為contains方法容易讓人引起誤解。
2. 同步性:Hashtable 的方法是 Synchronize 的,線程安全;而 HashMap 是線程不安全的,不是同步的。所以只有一個線程的時候使用hashMap效率要高。
3. 值:HashMap對象的key、value值均可為null。HahTable對象的key、value值均不可為null。
4. 容量:HashMap的初始容量為16,Hashtable初始容量為11,兩者的填充因子默認都是0.75。
5. HashMap擴容時是當前容量翻倍即:capacity * 2,Hashtable擴容時是容量翻倍+1 即:capacity * 2+1。
HashSet 底層就是基于 HashMap 實現的。只不過HashSet里面的HashMap所有的value都是同一個Object而已,因此HashSet也是非線程安全的。
1. HashMap通過hashcode對其內容進行快速查找,而TreeMap中所有的元素都保持著某種固定的順序,如果你需要得到一個有序的結果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。
2. 在Map 中插入、刪除和定位元素,HashMap是最好的選擇。但如果您要按自然順序或自定義順序遍歷鍵,那么TreeMap會更好。使用HashMap要求添加的鍵類明確定義了hashCode()和 equals()的實現。
每當向數組中添加元素時,都要去檢查添加后元素的個數是否會超出當前數組的長度,如果超出,數組將會進行擴容,以滿足添加數據的需求。數組擴容通過ensureCapacity(int minCapacity)方法來實現。在實際添加大量元素前,我也可以使用ensureCapacity來手動增加ArrayList實例的容量,以減少遞增式再分配的數量。 數組進行擴容時,會將老數組中的元素重新拷貝一份到新的數組中,每次數組容量的增長大約是其原容量的1.5倍。這種操作的代價是很高的,因此在實際使用時,我們應該盡量避免數組容量的擴張。當我們可預知要保存的元素的多少時,要在構造ArrayList實例時,就指定其容量,以避免數組擴容的發生。或者根據實際需求,通過調用ensureCapacity方法來手動增加ArrayList實例的容量。
ArrayList也采用了快速失敗的機制,通過記錄modCount參數來實現。在面對并發的修改時,迭代器很快就會完全失敗,而不是冒著在將來某個不確定時間發生任意不確定行為的風險。