文章目錄
1、Iterator、ListIterator
1.1、Iterator
迭代器用於遍歷集合
它包含三個方法:
修飾與類型 | 方法與描述 |
---|---|
boolean |
hasNext() 如果仍有元素可以迭代,則返回true。 |
E |
next() 返回迭代的下一個元素。 |
void |
remove() 從迭代器指向的 collection 中移除迭代器返回的最後一個元素(可選操作)。 |
也就說,只要通過該接口就可以取出Collection集合中的元素,至於每一個具體的容器依據自己的數據結構,如何實現的具體取出細節,這個不用關心,這樣就降低了取出元素和具體集合的耦合性。
遍歷:
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("abc0");
list1.add("abc1");
list1.add("abc2");
// while循環方式遍歷
Iterator it1 = list1.iterator();
while (it1.hasNext()) {
System.out.println(it1.next());
}
// for循環方式遍歷
for (Iterator it2 = list1.iterator(); it2.hasNext(); ) {
System.out.println(it2.next());
}
}
使用Iterator迭代器進行刪除集合元素,則不會出現併發修改異常。
因爲:在執行remove操作時,同樣先執行checkForComodification(),然後會執行ArrayList的remove()方法,該方法會將modCount值加1,這裏我們將expectedModCount=modCount,使之保持統一。
1.2、ListIterator
功能更加強大,它繼承於Iterator接口,只能用於各種List類型的訪問。可以通過調用listIterator()方法產生一個指向List開始處的ListIterator, 還可以調用listIterator(n)方法創建一個一開始就指向列表索引爲n的元素處的ListIterator。
特點:
- 允許前後遍歷
- 遍歷時修改元素(set)
- 遍歷時獲取迭代器當前遊標所在位置
修飾與類型 | 方法與描述 |
---|---|
void |
add(E e) 將指定的元素插入到列表 (可選操作)。 |
boolean |
hasNext() 如果此列表迭代器在前進方向還有更多的元素時,返回 true 。 |
boolean |
hasPrevious() 如果此列表迭代器在相反方向還有更多的元素時,返回 true 。 |
E |
next() 返回列表中的下一個元素和光標的位置向後推進。 |
int |
nextIndex() 返回調用 next() 後返回的元素索引。 |
E |
previous() 返回列表中的上一個元素和光標的位置向前移動。 |
int |
previousIndex() 返回調用previous() 後返回的元素索引 。 |
void |
remove() 刪除列表中調用next() 或previous() 的返回最後一個元素。 |
void |
set(E e) 用指定元素替換列表中調用next() 或previous() 的返回最後一個元素。 |
2、Collection
Java的集合類主要由兩個接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,這兩個接口又包含了一些子接口或實現類。
Collection集合主要有List和Set兩大接口
-
List:有序(元素存入集合的順序和取出的順序一致),元素都有索引。元素可以重複。
-
Set:無序(存入和取出順序有可能不一致),不可以存儲重複元素。必須保證元素唯一性。
2.1、List
常用方法
ArrayList、LinkedList、Vector 的區別
ArrayList | LinkedList | Vector | |
---|---|---|---|
底層實現 | 數組 | 雙向鏈表 | 數組 |
同步性及效率 | 不同步,非線程安全,效率高,支持隨機訪問 | 不同步,非線程安全,效率高 | 同步,線程安全,效率低 |
特點 | 查詢快,增刪慢 | 查詢慢,增刪快 | 查詢快,增刪慢 |
默認容量 | 10 | / | 10 |
擴容機制 | int newCapacity = oldCapacity + (oldCapacity >> 1);//1.5 倍 | / | 2 倍 |
- 對於隨機訪問,數組的效率肯定是優於鏈表的
- LinkedList 不會出現擴容的問題,所以比較適合隨機位置增、刪。但是其基於鏈表實現,所以在定位時需要線性掃描,效率比較低。
- 當操作是在一列數據的後面添加數據而不是在前面或中間,並且需要隨機地訪問其中的元素時,使用ArrayList會提供比較好的性能;
- 當你的操作是在一列數據的前面或中間添加或刪除數據,並且按照順序訪問其中的元素時,就應該使用LinkedList了。
2.1.1、數組轉ArrayList問題
Arrays.asList轉換得到的ArrayList不是java.util.ArrayList
正確操作
public static void main(String[] args) {
String[] arr = {"abc", "kk", "qq"};
// 使用new ArrayList包裹一層
List<String> list = new ArrayList<>(Arrays.asList(arr));
list.add("bb");
}
2.1.2、ArrayList擴容機制(大數據插入耗時問題)
https://blog.csdn.net/qq_41653935/article/details/105715617
2.2、Set (HashSet如何檢查重複)
Set集合元素無序(存入和取出的順序不一定一致),並且沒有重複對象。
Set的主要實現類:HashSet, TreeSet。
HashSet、TreeSet、LinkedHashSet的區別
HashSet | TreeSet | LinkedHashSet | |
---|---|---|---|
底層實現 | HashMap | 紅黑樹 | LinkedHashMap |
重複性 | 不允許重複 | 不允許重複 | 不允許重複 |
有無序 | 無序 | 有序,支持兩種排序方式,自然排序和定製排序,其中自然排序爲默認的排序方式。 | 有序,以元素插入的順序來維護集合的鏈接表 |
時間複雜度 | add(),remove(),contains()方法的時間複雜度是O(1) | add(),remove(),contains()方法的時間複雜度是O(logn) | LinkedHashSet在迭代訪問Set中的全部元素時,性能比HashSet好,但是插入時性能稍微遜色於HashSet,時間複雜度是 O(1)。 |
同步性 | 不同步,線程不安全 | 不同步,線程不安全 | 不同步,線程不安全 |
null值 | 允許null值 | 不支持null值,會拋出 java.lang.NullPointerException 異常。因爲TreeSet應用 compareTo() 方法於各個元素來比較他們,當比較null值時會拋出 NullPointerException異常。 | 允許null值 |
比較 | equals() | compareTo() | equals() |
HashSet如何檢查重複
HashSet會先計算對象的hashcode值來判斷對象加入的位置,同時也會與其他加入的對象的hashcode值作比較,如果沒有相符的hashcode,HashSet會假設對象沒有重複出現。但是如果發現有相同hashcode值的對象,這時會調用equals()方法來檢查hashcode相等的對象是否真的相同。如果兩者相同,HashSet就不會讓加入操作成功。
3、Map
Map 是一種把鍵對象和值對象映射的集合,它的每一個元素都包含一對鍵對象和值對象。
Map 的常用實現類:HashMap、TreeMap、HashTable、LinkedHashMap、ConcurrentHashMap
3.1、HashMap、HashTable、TreeMap的區別
- TreeMap:基於紅黑樹實現。
- HashMap:基於哈希表實現。
- HashTable:和 HashMap 類似,但它是線程安全的,這意味着同一時刻多個線程可以同時寫入 HashTable 並且不會導致數據不一致。它是遺留類,不應該去使用它。現在可以使用ConcurrentHashMap 來支持線程安全,並且 ConcurrentHashMap 的效率會更高,因爲 ConcurrentHashMap 引入了分段鎖。
- LinkedHashMap:使用雙向鏈表來維護元素的順序,順序爲插入順序或者最近最少使用(LRU)順序。
HashMap | HashTable | TreeMap | |
---|---|---|---|
底層實現 | 哈希表(數組+鏈表+紅黑樹) | 哈希表(數組+鏈表) | 紅黑樹 |
同步性 | 線程不同步 | 同步 | 線程不同步 |
null值 | 允許 key 和 Vale 是 null,但是隻允許一個 key 爲 null,且這個元素存放在哈希表 0 角標位置 | 不允許key、value 是 null | value允許爲null。 當未實現 Comparator 接口時,key 不可以爲null 當實現 Comparator 接口時,若未對 null 情況進行判斷,則可能拋 NullPointerException 異常。如果針對null情況實現了,可以存入,但是卻不能正常使用get()訪問,只能通過遍歷去訪問。 |
hash | 使用hash(Object key)擾動函數對 key 的 hashCode 進行擾動後作爲 hash 值 | 直接使用 key 的 hashCode() 返回值作爲 hash 值 | |
容量 | 容量爲 2^4 且容量一定是 2^n | 默認容量是11,不一定是 2^n | |
擴容 | 兩倍,且哈希桶的下標使用 &運算代替了取模 | 2倍+1,取哈希桶下標是直接用模運算 |
3.2、HashMap—JDK1.8(紅黑樹)
不同 | JDK 1.7 | JDK 1.8 |
---|---|---|
存儲結構 | 數組 + 鏈表 | 數組 + 鏈表 + 紅黑樹 |
初始化方式 | 單獨函數:inflateTable() |
直接集成到了擴容函數resize() 中 |
hash值計算方式 | 擾動處理 = 9次擾動 = 4次位運算 + 5次異或運算 | 擾動處理 = 2次擾動 = 1次位運算 + 1次異或運算 |
存放數據的規則 | 無衝突時,存放數組;衝突時,存放鏈表 | 無衝突時,存放數組;衝突 & 鏈表長度 < 8:存放單鏈表;衝突 & 鏈表長度 > 8:樹化並存放紅黑樹 |
插入數據方式 | 頭插法(先講原位置的數據移到後1位,再插入數據到該位置) | 尾插法(直接插入到鏈表尾部/紅黑樹) |
擴容後存儲位置的計算方式 | 全部按照原來方法進行計算(即hashCode ->> 擾動函數 ->> (h&length-1)) | 按照擴容後的規律計算(即擴容後的位置=原位置 or 原位置 + 舊容量) |
紅黑樹動畫演示,請看大佬博客: