java數據結構——必學容器知識

1、Iterator、ListIterator

1.1、Iterator

迭代器用於遍歷集合
它包含三個方法:

修飾與類型 方法與描述
boolean hasNext() 如果仍有元素可以迭代,則返回true。
E next() 返回迭代的下一個元素。
void remove() 從迭代器指向的 collection 中移除迭代器返回的最後一個元素(可選操作)。
每一個集合都有自己的數據結構(就是容器中存儲數據的方式),都有特定的取出自己內部元素的方式。爲了便於操作所有的容器,取出元素。將容器內部的取出方式按照一個統一的規則向外提供,這個規則就是Iterator接口,使得對容器的遍歷操作與其具體的底層實現相隔離,達到解耦的效果。

也就說,只要通過該接口就可以取出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 原位置 + 舊容量)

紅黑樹動畫演示,請看大佬博客:

https://blog.csdn.net/qq_26803795/article/details/106258368

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章