Java面試題-集合

Java面試題-集合

  • 1、什么是集合?
  • 2、集合和數組的區別是什么?
  • 3、集合有哪些特點?
  • 4、常用的集合類有哪些?
  • 5、List, Set, Map三者的區別?
  • 6、說說集合框架底層數據結構?
  • 7、線程安全的集合類有哪些?
  • 8、說說集合的快速失敗機制"fail-fast"?
  • 9、怎么確保一個集合不能被修改?
  • 10、說說你對Collection接口的了解?
  • 11、Iterator迭代器是什么?
  • 12、Iterator怎么使用?有什么特點?
  • 13、如何邊遍歷邊移除Collection中的元素?
  • 14、遍歷一個List有哪些不同的方式?每種方法的實現原理是什么?
  • 15、List遍歷的最佳實踐是什么?
  • 16、說一下 ArrayList 的優缺點?
  • 17、如何實現數組和List之間的轉換?
  • 18、ArrayList 和 LinkedList 的區別是什么?
  • 19、ArrayList 和 Vector的區別是什么?
  • 20、多線程場景下如何使用ArrayList?
  • 21、為什么 ArrayList 的 elementData要加上transient修飾?
  • 22、List 和 Set 的區別有哪些?
  • 23、說一下HashSet的實現原理?
  • 24、HashSet如何檢查重復?HashSet是如何保證數據不可重復的?
  • 25、HashSet與HashMap有什么區別?
  • 26、什么是Hash算法?
  • 27、鏈表是什么?
  • 28、說說鏈表的分類和優缺點?
  • 29、說一下HashMap的實現原理?
  • 30、HashMap在JDK1.7和JDK1.8中有哪些不同?
  • 31、什么是紅黑樹?
  • 32、HashMap的put方法的具體流程是怎樣的?
  • 33、HashMap的擴容操作是怎么實現的?
  • 34、HashMap是怎么解決哈希沖突的?
  • 35、什么是哈希?
  • 36、什么是哈希沖突?
  • 37、說說HashMap的數據結構?
  • 38、能否使用任何類作為Map的key?
  • 39、為什么HashMap中String、Integer這樣的包裝類適合作為Key?
  • 40、如果要使用Object作為HashMap的Key,應該怎么辦?
  • 41、HashMap為什么不直接使用hashCode()處理后的哈希值直接作為table的下標?
  • 42、HashMap的長度為什么是2的冪次方?
  • 43、HashMap與HashTable 有什么區別?
  • 44、什么是TreeMap?
  • 45、如何決定使用 HashMap 還是TreeMap?
  • 46、HashMap 和 ConcurrentHashMap 的區別?
  • 47、ConcurrentHashMap 和 Hashtable的區別?
  • 48、ConcurrentHashMap 底層具體實現原理是什么?
  • 49、Array 和 ArrayList 有何區別?
  • 50、comparable 和 comparator的區別?
  • 51、Collection 和 Collections有什么區別?
  • 52、TreeMap 和 TreeSet 在排序時如何比較元素?
  • 53、Collections 工具類中的sort()方法如何比較元素?

1、什么是集合?

集合(Collection)是Java中一種用于存儲對象的容器,它可以包含多個對象,這些對象可以是相同類型或不同類型(依賴于具體使用的集合類型)。Java集合框架提供了一系列的接口和實現(如ListSetMap等),用于存儲和操作對象群集,支持各種操作,如添加、刪除、遍歷、排序等。

2、集合和數組的區別是什么?

  • 大小:數組大小固定,一旦創建無法改變;集合大小可變,可以根據需要動態增減。
  • 類型限制:數組可以包含基本數據類型和對象;集合只能包含對象。
  • 功能:集合提供了更多復雜的數據操作方法(如添加、刪除、插入、遍歷等),而數組操作相對簡單。
  • 性能:對于固定大小和類型的數據操作,數組通常會比集合更高效。

3、集合有哪些特點?

  • List:有序集合(也稱為序列),能夠精確控制每個元素的插入位置,可以包含重復元素。
  • Set:不允許重復的集合,沒有索引,不能包含相同的元素。
  • Map:鍵值對集合,持有鍵(唯一)到值的映射,一個鍵最多只能映射到一個值。
  • Queue:隊列,按照先進先出的原則對元素進行處理。
  • Deque:雙端隊列,允許我們從兩端添加或刪除元素。

4、常用的集合類有哪些?

  • List接口的實現類ArrayListLinkedListVector
  • Set接口的實現類HashSetLinkedHashSetTreeSet
  • Map接口的實現類HashMapLinkedHashMapTreeMapHashtable
  • Queue接口的實現類PriorityQueueArrayDeque
  • 特殊集合類Stack(繼承自Vector)、EnumSetEnumMap(轉為枚舉類型設計)。

5、List, Set, Map三者的區別?

  • List:有序集合,可包含重復元素,通過索引訪問元素。
  • Set:無序集合,不可包含重復元素,主要用于確保元素唯一性。
  • Map:鍵值對集合,存儲唯一鍵與值的映射,鍵不可重復,值可以重復。

6、說說集合框架底層數據結構?

  • ArrayList:動態數組(Resizable Array)。
  • LinkedList:雙向鏈表。
  • HashSet:基于哈希表(實際上是HashMap的一個實例)。
  • LinkedHashSet:哈希表+雙向鏈表,維護元素插入順序。
  • TreeSet:紅黑樹(自平衡的二又查找樹)。
  • HashMap:哈希表,存儲鍵值對。
  • LinkedHashMap:哈希表+雙向鏈表,維護鍵值對的插入順序或訪問順序。
  • TreeMap:紅黑樹,根據鍵的自然順序或Comparator排序。
  • PriorityQueue:優先隊列,基于堆結構。
  • ArrayDeque:數組或循環數組實現的雙端隊列。

7、線程安全的集合類有哪些?

  • Vector
  • Hashtable
  • Stack
  • ConcurrentHashMap
  • CopyOnWriteArrayList
  • CopyOnWriteArraySet
  • BlockingQueue的實現類(ArrayBlockingQueueLinkedBlockingQueue等)
  • ConcurrentLinkedQueue
  • ConcurrentLinkedDeque

8、說說集合的快速失敗機制"fail-fast"?

快速失敗(fail-fast)機制是Java集合框架的一種錯誤檢測機制,它用于在迭代器的操作過程中,如果檢測到集合被結構性修改(增加、刪除元素等),則立即拋出ConcurrentModificationException。這樣做可以避免在迭代過程中因為集合的結構改變而產生不可預測的行為。快速失敗行為主要通過集合的modCount(修改計數器)實現,迭代器創建時會記錄下modCount,每次迭代時檢查該值是否發生變化。

9、怎么確保一個集合不能被修改?

可以使用Collections類提供的unmodifiableCollectionunmodifiableLlistunmodifiableSetunmodifiableMap等方法將集合包裝為不可修改的視圖。嘗試修改這些不可修改的視圖將拋出UnsupportedOperationException。例如:

List<String> list = new ArrayList<>();
List<String> unmodifiableList = Collections.unmodifiableList(list);

通過這種方式,原始集合list的任何修改都不會反映在unmodifiableList上,且unmodifiableList不允許任何修改操作。

10、說說你對Collection接口的了解?

Collection接口是Java集合框架的根接口,不繼承于其他接口。它提供了對集合對象進行基本操作的通用接口方法,如添加、刪除、清空集合,以及檢查集合的大小、判斷集合是否為空或是否包含某個對象等。Collection接口是ListSetQueue接口的父接口,因此這些接口繼承并擴展了Collection接口的功能,以支持更具體的集合操作。Collection接口本身并不提供直接的實現,它的實現是通過其子接口(如ListSet等)和類(如 ArrayListHashSet等)提供的。

11、Iterator迭代器是什么?

Iterator是一個接口,提供了遍歷集合(如ListSet)的標準方法,包括檢查序列中是否還有元素(hasNext())、獲取序列中的下一個元素(next())和從集合中刪除上一次通過next()方法返回的元素(remove())。Iterator使得客戶端代碼可以統一地遍歷集合,而不需要了解其底層的結構。

12、Iterator怎么使用?有什么特點?

使用Iterator的基本步驟如下:

  1. 通過調用集合的iterator()方法獲取迭代器實例。
  2. 使用hasNext()方法檢查集合中是否還有元素。
  3. 使用next()方法訪問集合中的下一個元素。
  4. 如需從集合中刪除元素,可調用remove()方法。

特點包括:

  • 通用性:為不同類型的集合提供了統一的遍歷方式。
  • 安全刪除:通過迭代器的remove()方法可以安全刪除集合中的元素,避免ConcurrentModificationException
  • 單向遍歷:迭代器僅支持向前遍歷集合,不支持反向遍歷。
  • 無法訪問索引:迭代器不提供獲取當前元素索引的方法。
List<String> list = new ArrayList<>();
Iterator<String> it = list.iterator();
while(it.hasNext()){String obj = it.next();System.out.println(obj);
}

13、如何邊遍歷邊移除Collection中的元素?

邊遍歷邊移除Collection中的元素,應使用Iteratorremove()方法。步驟如下:

  1. 獲取Collection的迭代器。
  2. 使用hasNext()檢查是否有更多元素。
  3. 使用next()方法獲取元素。
  4. 使用remove()方法刪除上一次通過next()方法訪問的元素。

示例代碼:

Iterator<Element> iterator = collection.iterator();
while (iterator.hasNext()) {Element element = iterator.next();//根據條件判斷是否需要刪除當前元素if (/*條件判斷*/) {iterator.remove();// 刪除元素}
}

這種方式安全,不會引發ConcurrentModificationException

14、遍歷一個List有哪些不同的方式?每種方法的實現原理是什么?

  1. for循環:通過索引直接訪問列表中的元素。實現原理是基于數組(如 ArrayList)或鏈表(如LinkedList)的結構,直接通過位置索引獲取元素。
for (int i = 0; i < list.size(); i++) {Element element = list.get(i);
}
  1. 增強for循環(for-each循環):簡化的遍歷方式,內部使用 Iterator 實現。適用于所有實現了Iterable接口的集合類。
for (Element element : list){//使用element
}
  1. lterator遍歷:使用 Iterator 接口提供的hasNext()next()方法遍歷元素。這是一種更通用的遍歷方式,允許在遍歷過程中安全地移除元素。
Iterator<Element> iterator - list.iterator()2while (iterator.hasNext(){
Element element - iterator.next()4}
  1. Listlterator遍歷:僅適用于List接口。ListIterator提供了向前和向后遍歷列表的能力,同時支持添加、替換和刪除操作。
ListIterator<Element> listIterator = list.listIterator();
while (listIterator.hasNext()) {Element element = listIterator.next();
}
  1. Java 8 Stream API:提供了一種更聲明式的處理集合的方法,支持順序和并行遍歷,以及提供了豐富的操作如篩選、映射、排序等。
list.stream().forEach(element -> {//使用element
});
  • for循環增強for循環依賴于集合的內部結構進行直接訪問或隱式使用Iterator
  • Iterator遍歷Listlterator遍歷提供了顯式的迭代器對象,允許在遍歷時修改集合。
  • Stream APl使用函數式編程方式,可以簡化代碼并利用多核架構。

15、List遍歷的最佳實踐是什么?

Java中List遍歷的最佳實踐取決于具體的使用場景和需求:

  • 讀取操作:如果僅進行遍歷讀取,推薦使用增強for循環(for-each循環),因為它簡潔易讀。
  • 性能敏感:對于ArrayList,直接使用基于索引的for循環可能更高效,尤其是在大列表中;對于LinkedList,使用增強for循環或Iterator遍歷效率更高。
  • 需要修改列表:如果在遍歷過程中需要修改列表(添加、刪除、替換元素),使用IteratorListIterator,它們提供了安全修改集合的方法。
  • Java 8及以上:考慮使用Stream API,它提供了更高級的操作(如過濾、轉換等),并可以利用并行處理來提高性能。

16、說一下 ArrayList 的優缺點?

優點

  1. 隨機訪問效率高:基于動態數組實現,支持快速隨機訪問,通過索引直接訪問元素的時間復雜度為O(1)。
  2. 動態擴容:能根據需要自動增加存儲容量,使用方便。
  3. API豐富:提供了豐富的方法用于操作列表中的元素,包括添加、刪除、查找、遍歷等。

缺點

  1. 插入和刪除效率低:在列表的中間或開始位置添加或刪除元素需要移動后續所有元素,時間復雜度為O(n)。
  2. 內存開銷:在動態擴容時,需要重新分配數組并復制舊數組的內容到新數組,可能會有額外的內存開銷。
  3. 容量浪費:自動擴容機制可能會導致實際分配的容量大于實際所需,造成空間浪費。

17、如何實現數組和List之間的轉換?

數組轉List
使用Arrays.asList(T...a)方法將數組轉換為固定大小的列表。

String[] array = {"a","b","c"};
List<String> list = Arrays.asList(array);

List轉數組
使用ListtoArray(T[] a)方法將列表轉換為數組。

List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
String[] array = list.toArray(new String[0]);

注意:Arrays.aslList返回的列表是固定大小的,對其進行添加或刪除操作會拋出UnsupportedOperationException。若要創建一個可變的列表,可以使用新的ArrayList<>(Arrays.asList(array))

18、ArrayList 和 LinkedList 的區別是什么?

  • 內部結構ArrayList基于動態數組實現,支持快速隨機訪問;LinkedList基于雙向鏈表實現,每個元素都有指向前后元素的引用。
  • 性能ArrayList的隨機訪問速度快(O(1)),但在列表中間或開頭插入和刪除元素的速度較慢(O(n));LinkedList的插入和刪除操作速度快(O(1)),但隨機訪問速度慢(O(n))。
  • 內存占用ArrayList因為大小可變,可能會有額外的內存占用;LinkedList對于每個元素都需要額外的內存空間來存儲前后元素的引用。
  • 使用場景ArrayList適合頻繁的查找操作,LinkedList適合頻繁的插入和刪除操作。

19、ArrayList 和 Vector的區別是什么?

  • 同步性Vector是同步的,適用于多線程環境,但因為同步帶來額外開銷,性能較低;ArrayList是非同步的,適用于單線程環境,性能較高。
  • 數據增長ArrayList的數據增長率默認為50%,而Vector默認增長率是100%,也可以在創建時指定增長率。
  • 遺留性Vector是Java早期版本的一部分,被視為遺留類,ArrayList是Java集合框架(JCF)的一部分,是較新的、更輕量級的選擇。

20、多線程場景下如何使用ArrayList?

在多線程場景下使用ArrayList時,可以采取以下措施以確保線程安全:

  1. 使用**Collections.synchronizedList**方法:將ArrayList包裝為同步的列表。
List<String> syncList = Collections.synchronizedList(new ArrayList<String>());
  1. 使用**CopyOnWriteArrayList**:適用于讀多寫少的并發場景,因為每次修改(添加、刪除、設置等)都會復制整個基礎數組。
List<String> cowList = new CopyOnWriteArrayList<>();
  1. 手動同步:在對ArrayList進行操作的代碼塊前后使用synchronized關鍵字手動同步。
synchronized(list) {// list操作
}

21、為什么 ArrayList 的 elementData要加上transient修飾?

ArrayListelementData數組被聲明為transient是為了在序列化過程中控制哪些數據被序列化,避免將數組中的空元素也序列化。這樣做可以最小化序列化存儲空間的使用,只序列化數組中實際存儲的元素,而不是整個底層數組。在反序列化時,只根據存儲的元素重新構建數組,從而提高了效率和節省了存儲空間。

22、List 和 Set 的區別有哪些?

  • 元素唯一性List允許重復的元素,按照插入順序排序;Set不允許重復的元素,通常不保證元素的順序(除LinkedHashSet)。
  • 索引訪問List支持通過索引位置訪問元素;Set不支持索引訪問。
  • 接口實現ListSet是Java集合框架中的兩個不同的接口,分別有各自的實現類,如ArrayListLinkedListList接口的實現,而HashSetLinkedHashSetTreeSetSet接口的實現。

23、說一下HashSet的實現原理?

HashSet是基于HashMap實現的。它使用HashMap的鍵來存儲元素,每個元素都映射到一個固定的虛擬值(HashMap的值部分)。HashSet利用HashMap鍵的唯一性來確保其元素的唯一性,并且通過哈希表實現,因此具有很高的查找和插入效率。當向HashSet添加元素時,實際上是將元素作為HashMap的鍵添加,而對應的值則是一個預定義的靜態的新對象(因為HashMap是鍵值對映射,而HashSet只關心鍵)。

24、HashSet如何檢查重復?HashSet是如何保證數據不可重復的?

HashSet檢查重復并保證數據不可重復的機制基于其底層的 HashMap實現。當向HashSet添加元素時,實際上是將該元素作為HashMap的鍵:

  1. 哈希值計算:首先,利用元素的hashCode()方法計算其哈希值,這個哈希值用于定位在哈希表中的存儲位置。
  2. 鍵的唯一性HashMap保證了每個鍵的唯一性。如果插入的元素(作為HashMap的鍵)的哈希值與已存在的某個元素的哈希值相同,HashMap會進一步調用equals()方法來檢查這兩個元素是否真的相等。
    1. 如果equals()返回 true,則認為這兩個元素相等,新的插入操作不會發生,從而保證了HashSet 中元素的唯一性。
    2. 如果equals()返回false,則認為這兩個元素雖然哈希值相同但不等(發生了哈希沖突),HashMap將通過某種沖突解決機制(如鏈表或紅黑樹)來存儲這兩個元素,保證它們都被保存在結構中。

通過這種方式,HashSet利用HashMap的鍵的唯一性來保證自身存儲的元素的唯一性。

25、HashSet與HashMap有什么區別?

  • 用途差異HashSet是一個不允許重復元素的集合,用于存儲唯一的值;HashMap是一個鍵值對集合,存儲鍵值映射,允許通過鍵快速訪問數據。
  • 數據結構HashSet內部實際上是通過HashMap實現的,它存儲元素作為HashMap的鍵,所有的值都映射到一個預定義的常量上;HashMap存儲鍵值對,每個鍵映射到一個具體的值。
  • 方法和功能HashSet主要提供添加、刪除、包含等操作,關注點在于元素的唯一性;而HashMap提供了更豐富的操作,如獲取和設置鍵值對,檢查鍵或值是否存在等。
  • 性能:在使用上,兩者的性能考量主要依賴于hashCode()方法的實現,以及沖突解決策略,但HashSet主要關注集合操作,而HashMap關注鍵值對的映射和訪問。

26、什么是Hash算法?

Hash算法是一種將任意長度的輸入(或稱為消息)通過散列算法處理,轉換成固定長度輸出的過程。該輸出稱為散列值或哈希值。Hash算法的特點包括:對于任何給定的輸入,輸出都是確定的;不同的輸入盡可能產生不同的輸出(盡管會有哈希碰撞的可能性);算法執行過程是單向的,即從哈希值不能逆向推導出原始數據。Hash算法廣泛應用于數據檢索、加密、數據完整性驗證等領域。

27、鏈表是什么?

鏈表是一種數據結構,由一系列節點組成,每個節點包含數據部分和一個或多個指向其他節點的引用(指針)。鏈表允許高效的元素插入和刪除操作,因為這些操作只需要改變相鄰節點的引用。

28、說說鏈表的分類和優缺點?

鏈表主要分為三種類型:

  1. 單向鏈表:每個節點包含數據和指向下一個節點的指針。可以高效地進行插入和刪除操作,但只能向一個方向遍歷。
  2. 雙向鏈表:每個節點包含數據、指向前一個節點的指針和指向下一個節點的指針。可以向兩個方向遍歷,插入和刪除操作相對更靈活和高效。
  3. 循環鏈表:單向或雙向鏈表的變種,其中最后一個節點的下一個指針指向第一個節點,形成一個環。便于從任何節點開始遍歷整個鏈表。

優點

  • 動態數據結構:鏈表可以根據需要動態地分配內存,不需要在創建時確定大小。
  • 插入和刪除高效:相較于數組,鏈表在插入和刪除節點時不需要移動其他元素,只需修改指針即可。

缺點

  • 訪問時間:相較于數組,鏈表的訪問時間較長,不能直接通過索引訪問元素,需要從頭開始遍歷。
  • 內存占用:由于每個元素需要額外存儲指針,所以相較于數組鏈表的內存占用更大。
  • 逆向遍歷困難:單向鏈表的逆向遍歷非常困難,需要額外的數據結構或算法。

29、說一下HashMap的實現原理?

HashMap是基于哈希表實現的。核心原理如下:

  1. 數據存儲HashMap存儲鍵值對,其中鍵是唯一的。
  2. 哈希函數:使用鍵的hashCode()方法計算哈希碼,然后通過散列函數將此哈希碼映射到哈希表中的一個位置(桶)以決定鍵值對的存儲位置。
  3. 處理哈希沖突:當不同的鍵產生相同的哈希碼(哈希沖突)時,HashMap采用鏈表(JDK 1.8之前)或紅黑樹(JDK 1.8及以后,鏈表長度超過一定閾值時轉換為紅黑樹)來存儲相同哈希碼的不同鍵值對。
  4. 動態擴容:當哈希表中的數據量超過負載因子(load factor)和當前容量的乘積時,哈希表將進行擴容(通常是翻倍),然后重新計算已有的鍵值對在新哈希表中的位置。
  5. 鍵值訪問:通過鍵的哈希碼快速定位到其值的存儲位置,實現高效的數據查找、插入和刪除操作。

30、HashMap在JDK1.7和JDK1.8中有哪些不同?

主要區別在于內部數據結構和擴容機制的改進:

  1. 內部數據結構
    1. JDK 1.7:使用數組+鏈表的結構。當多個鍵的哈希值相同(哈希沖突)時,會在相同哈希值的桶位置形成一個鏈表。
    2. JDK 1.8:引入了數組+鏈表+紅黑樹的結構。當鏈表長度超過閾值(默認為8)時,鏈表轉換為紅黑樹,以減少搜索時間。
  2. 擴容機制
    1. JDK 1.7:在擴容時,新的數組大小是原來的兩倍,所有元素需要重新計算哈希值并分配到新的桶中,這個過程效率較低。
    2. JDK 1.8:優化了擴容算法,通過保留原有節點的順序,減少了重新計算哈希值的需要,只需判斷節點存儲在原位置還是移動到新的位置,提高了擴容的效率。

這些改進提升了HashMap在處理大量數據時的性能,尤其是減少了搜索時間和擴容成本。

31、什么是紅黑樹?

紅黑樹是一種自平衡的二又搜索樹,它通過每個節點增加一個存儲位表示節點的顏色(紅或黑),并通過一系列的規則和旋轉操作來保持樹的平衡。這些規則包括:

  1. 每個節點要么是紅的,要么是黑的。
  2. 根節點是黑的。
  3. 所有葉子節點(NIL節點,空節點)都是黑的。
  4. 每個紅節點的兩個子節點都是黑節點(從每個葉子到根的所有路徑上不能有兩個連續的紅節點)。
  5. 從任一節點到其每個葉子的所有簡單路徑都包含相同數目的黑色節點。

紅黑樹的這些性質確保了最長的路徑不會超過最短的路徑的兩倍,因而接近于平衡。這保證了紅黑樹在插入、刪除和查找操作中都能保持較高的性能,最壞情況下的時間復雜度為O(log n)。

32、HashMap的put方法的具體流程是怎樣的?

HashMapput方法流程(基于JDK 1.8)如下:

  1. 計算鍵的哈希值:首先,使用鍵對象的hashCode()方法計算哈希值,然后通過哈希函數處理這個哈希值以減少碰撞。
  2. 定位桶位置:使用處理后的哈希值與數組長度減一進行位運算(假設數組長度是2的幕),確定鍵值對應該存放的桶(數組索引)位置。
  3. 檢查鍵是否存在
    1. 如果目標桶為空,直接在該位置創建一個節點存儲鍵值對;
    2. 如果目標桶不為空(即發生了哈希碰撞)則遍歷該桶(可能是鏈表或紅黑樹):
      1. 如果鍵已存在,更新其對應的值;
      2. 如果鍵不存在,則在適當的位置添加一個新節點(鏈表末尾或紅黑樹中)。
  4. 樹化檢查:如果鏈表長度超過閾值(默認為8),并且數組長度大于或等于64,那么將鏈表轉換為紅黑樹,以提高后續的查找和插入效率。
  5. 檢查是否需要擴容:如果當前HashMap的大小超過了容量與負載因子(默認為0.75)的乘積,那么進行擴容(容量翻倍)并重新分配所有的鍵值對。
  6. 返回值:如果插入的鍵已經存在,則返回舊值;如果是新插入的鍵,則返回null

33、HashMap的擴容操作是怎么實現的?

HashMap的擴容操作主要包括以下步驟:

  1. 創建新數組:創建一個新的數組,大小通常是原數組大小的兩倍。
  2. 重新計算索引:遍歷原數組中的所有元素,對每個元素重新計算其在新數組中的位置。這是通過使用元素的哈希值與新數組的長度進行位運算來完成的。
  3. 重新分配元素:根據重新計算得到的索引,將所有元素重新放置到新數組的相應位置上。如果原位置是鏈表或紅黑樹,則需要遍歷這些結構,并對每個節點進行同樣的重新索引操作。
  4. 更新引用:最后,將HashMap的內部引用從舊數組更新為指向新數組。

34、HashMap是怎么解決哈希沖突的?

HashMap解決哈希沖突的主要方式是通過鏈地址法,即在發生沖突的桶位置使用鏈表(JDK 1.8之前)或紅黑樹(JDK 1.8及以后,在鏈表長度超過一定閾值時,鏈表轉換為紅黑樹)來存儲多個元素。這樣,即使多個鍵映射到同一個桶(索引位置),它們也可以通過鏈表或紅黑樹結構被有效地存儲和檢索。在查找、插入或刪除操作時,HashMap會先定位到桶的位置,然后遍歷鏈表或紅黑樹來進行具體操作。這種方法允許多個鍵共享同一個桶位置,同時保持操作的效率。

35、什么是哈希?

哈希是一種將任意長度的輸入通過哈希函數轉換成固定長度輸出的過程,輸出結果稱為哈希值。哈希過程的主要特點是高效性和確定性,即相同的輸入總是產生相同的輸出,不同的輸入盡量產生不同的輸出。哈希廣泛應用于數據檢索、數據加密、數據完整性校驗等領域。

36、什么是哈希沖突?

哈希沖突是指兩個或多個不同的輸入值在經過哈希函數處理后得到了相同的哈希值的情況。由于哈希函數的輸出空間有限,而輸入空間可能是無限的,因此不同的輸入有可能映射到同一個輸出,即發生哈希沖突。處理哈希沖突是哈希表等數據結構設計中的一個重要考慮因素。

37、說說HashMap的數據結構?

HashMap的數據結構是一個結合了數組和鏈表(或紅黑樹)的復合結構。它使用一個數組來存儲數據,數組的每個槽位(桶)可以指向一個鏈表或紅黑樹的結構,用于存儲具有相同哈希值(經過哈希函數處理后的索引)的多個鍵值對。在JDK 1.8及以后版本中,當鏈表長度超過一定閾值時,鏈表會被轉換為紅黑樹,以提高搜索效率。

38、能否使用任何類作為Map的key?

是的,理論上可以使用任何類作為Mapkey。但是,為了確保Map正常工作,該類應正確重寫hashCode()equals()方法。這是因為Map(特別是HashMap)依賴這兩個方法來確定鍵的唯一性并維護鍵值對。如果這兩個方法沒有被適當地重寫,可能導致Map表現出不正確的行為,如丟失數據或無法檢索到數據。

39、為什么HashMap中String、Integer這樣的包裝類適合作為Key?

StringInteger等包裝類適合作為HashMap中的Key,因為它們已經正確地重寫了hashCode()equals()方法,確保了相同的實例會有相同的哈希碼,且只有當兩個對象邏輯上等價時,equals()方法才返回 true。這使得這些類型的對象能夠保持鍵的唯一性和一致性,從而在HashMap中高效地存儲和檢索數據。

40、如果要使用Object作為HashMap的Key,應該怎么辦?

  1. 重寫**hashCode()**方法:確保邏輯上相等的對象返回相同的哈希碼。
  2. 重寫**equals()**方法:確保正確比較兩個鍵對象的邏輯等價性,即當且僅當兩個對象邏輯上相等時,equals()方法返回 true

這樣做是為了保證HashMap能夠正確地處理鍵的唯一性和檢索操作。未正確重寫這些方法可能導致HashMap行為不正確,比如查找失敗或意外的數據覆蓋。

41、HashMap為什么不直接使用hashCode()處理后的哈希值直接作為table的下標?

使用hashCode()處理后的哈希值直接作為數組下標的話,會面臨幾個問題:

  1. 數組大小限制:直接使用哈希值作為下標可能導致非常大的數組索引,遠遠超過HashMap的實際數組大小,這是不切實際的。
  2. 負數問題hashCode()方法返回的是int類型,可能是負數,而數組索引不能是負數。

為了解決這些問題,HashMap通過對哈希值進行額外的哈希函數處理,并與數組長度-1進行位運算(通常是與操作),來確保計算出來的索引在數組的有效范圍內,同時也幫助分散哈希值,減少哈希沖突。

42、HashMap的長度為什么是2的冪次方?

主要有兩個原因:

  1. 位運算效率:使用2的冪次作為數組長度,允許HashMap通過位運算(hashCode & (length-1))代替模運算來計算索引,大大提高了計算索引的效率。
  2. 均勻分布:2的冪次方作為長度可以幫助哈希值更均勻地分布在整個數組中,減少哈希沖突,從而提高HashMap的性能。

43、HashMap與HashTable 有什么區別?

  1. 線程安全HashTable是線程安全的,所有方法都是同步的;而HashMap是非線程安全的。
  2. 性能:因為HashTable的方法是同步的,所以在單線程環境下比HashMap性能低。
  3. 空值HashMap允許鍵和值為nullHashTable不允許鍵或值為null
  4. 迭代器HashMap的迭代器是快速失敗的(fail-fast),而HashTable的枚舉器不是。

44、什么是TreeMap?

TreeMap是基于紅黑樹(一種自平衡二叉查找樹)的Map接口實現。它能夠保持鍵的自然排序(根據其compareTo()方法)或根據構造時提供的Comparator進行排序。TreeMap實現了SortedMap 接口,因此它支持有序集合操作,如返回指定范圍的子映射、獲取第一個(最低)和最后一個(最高)鍵等。由于其基于紅黑樹,TreeMap的插入、刪除、查找等操作的時間復雜度為O(log n)。

45、如何決定使用 HashMap 還是TreeMap?

  • 性能需求:如果關注插入、刪除、查找的性能,并且不需要排序,HashMap(平均O(1)的時間復雜度)通常是更好的選擇。如果需要一個按照自然順序或自定義順序來遍歷鍵,TreeMap(保證了O(log n)的時間復雜度)可能更適合。
  • 排序需求:如果需要鍵值對保持有序,選擇TreeMap;如果不需要排序,HashMap效率更高。
  • 線程安全:如果需要線程安全,兩者都不是最佳選擇,可能需要考慮ConcurrentHashMapCollections.synchronizedMap包裝器。但就單個線程或已有外部同步措施情況而言,選擇依據是性能和排序需求。
  • 內存使用HashMap通常使用較少的內存,因為TreeMap需要維護紅黑樹的結構。

46、HashMap 和 ConcurrentHashMap 的區別?

  • 線程安全ConcurrentHashMap是線程安全的,提供了高效的并發訪問;而HashMap是非線程安全的,不適合在并發環境下使用而不進行外部同步。
  • 內部結構ConcurrentHashMap在內部使用了分段鎖(JDK1.7)或使用 CAS操作和synchronized關鍵字來減少鎖競爭(JDK1.8),從而提高并發訪問性能;HashMap沒有這樣的機制,其所有操作都不是同步的。
  • 迭代器弱一致性ConcurrentHashMap的迭代器提供弱一致性遍歷,而不是HashMap的快速失敗遍歷方式。
  • 性能:由于ConcurrentHashMap的并發優化,它在多線程環境下比同步的HashMap(如通過Collections.synchronizedMap包裝的HashMap)提供了更好的讀寫性能。
  • 功能差異ConcurrentHashMap移除了一些如contains方法,改為containsValuecontainsKey,更明確地表達了方法的意圖。

47、ConcurrentHashMap 和 Hashtable的區別?

  • 線程安全實現ConcurrentHashMap使用分段鎖(JDK 1.7) 和CAS操作(JDK 1.8及以后)來提高并發訪問性能,只鎖定部分數據結構;而Hashtable使用方法級的同步,每個方法都是同步的,鎖定整個數據結構,導致并發性能較低。
  • 性能:因為ConcurrentHashMap的并發優化,它在多線程環境下提供比 Hashtable 更高的讀寫性能。
  • 迭代器ConcurrentHashMap的迭代器提供弱一致性視圖,不會拋出ConcurrentModificationException;而Hashtable的枚舉器和迭代器在結構被并發修改時可能拋出此異常。
  • 空鍵和空值ConcurrentHashMap不允許鍵(keys)或值(values)為null;而Hashtable允許非空鍵和值。
  • 設計目的ConcurrentHashMap專為并發使用場景設計,提供了一些原子操作方法;Hashtable是早期Java版本的遺留類,主要用于單線程或通過外部同步機制在多線程中使用。

48、ConcurrentHashMap 底層具體實現原理是什么?

ConcurrentHashMap的底層實現原理在JDK1.7和JDK1.8中有所不同:

  • JDK 1.7ConcurrentHashMap使用分段鎖(Segment鎖)機制。它內部維護一個Segment數組,每個Segment實質上是一個小的哈希表,擁有自己的鎖。對ConcurrentHashMap的操作只會鎖定必要的 Segment,從而減少鎖競爭,提高并發效率。每個Segment負責管理它所包含的鍵值對。
  • JDK 1.8:改進了數據結構,去掉了Segment,直接使用節點數組加鏈表和紅黑樹的結構。通過使用synchronized 和 CAS(無鎖機制)來控制并發,同時使用了紅黑樹來優化長鏈表的查找時間。在JDK1.8中,ConcurrentHashMap在鏈表長度超過一定閾值時將鏈表轉換為紅黑樹,提高搜索效率。

在兩個版本中,ConcurrentHashMap都能夠通過細粒度的鎖控制或無鎖機制來實現高效的并發訪問,尤其是在讀操作遠遠多于寫操作的場景下表現良好。

49、Array 和 ArrayList 有何區別?

  • 類型Array是固定大小的,可以存儲基本類型或對象;ArrayList是可變大小的,只能存儲對象。
  • 大小Array的大小在創建時確定,不能動態改變;ArrayList的大小可動態增長。
  • 性能:對于基本類型數據,Array有更好的性能,因為ArrayList使用包裝類作為泛型存儲,有裝箱和拆箱的開銷。
  • 功能ArrayList提供了更多的方法和功能,如自動擴容、便捷的插入、刪除等操作。

50、comparable 和 comparator的區別?

  • Comparable:是一個對象自比較其自身與另一個對象的接口。如果一個類實現了Comparable接口,就意味著該類支持排序。Comparable接口有一個compareTo(Object o)方法,用于定義默認的排序方式。
  • Comparator:是一個比較器接口,可以定義兩個對象的比較方式。如果你想要控制某個類的排序邏輯,但是這個類不能修改或者你想要有多種排序方式,可以通過實現Comparator接口,使用它的compare(Object o1, Object o2)方法來實現。

簡而言之,Comparable用于實現自然排序,而Comparator用于實現自定義排序邏輯。

51、Collection 和 Collections有什么區別?

  • Collection:是一個接口,它是各種集合結構(如 listSet等)的根接口,提供了對集合對象進行基本操作的通用接口方法。
  • Collections:是一個包含有關集合操作的靜態方法的工具類,如排序、搜索等,用于操作或返回集合。

52、TreeMap 和 TreeSet 在排序時如何比較元素?

TreeMapTreeSet在排序元素時,可以依賴元素的自然排序或者通過提供一個自定義的Comparator對象來定義排序規則。

  • 自然排序:當沒有提供Comparator 時,元素類必須實現Comparable接口,并且compareTo(Object o)方法定義了其自然排序的邏輯。
  • 自定義排序:通過構造函數傳入一個Comparator對象,使用該比較器的compare(Object o1, Object o2)方法來比較元素。

53、Collections 工具類中的sort()方法如何比較元素?

Collections.sort()方法比較元素的方式取決于傳入的集合元素類型:

  • 如果集合元素實現了Comparable接口, sort()方法會使用這些元素的compareTo()方法進行自然排序。
  • 可以重載 sort() 方法,傳入一個自定義的Comparator 對象,這時候會使用該Comparator的compare()方法來比較元素,實現自定義排序。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/22340.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/22340.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/22340.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MeshFusion Pro : Ultimate Optimization Tool

MeshFusion Pro是Unity的強大優化工具,它使用一種高效的方法來組合對象,以減少繪制調用并提高FPS。 MeshFusion Pro可用于組合靜態對象以及LODGroups。您還可以創建動態組合對象,其中每個單獨的網格都可以在運行時移動,新的組合網格將自動更新。在保持單個網格自由度的同時…

【數據結構與算法 | 二叉樹篇】力扣101, 104, 111

1. 力扣101 : 對稱二叉樹 (1). 題 給你一個二叉樹的根節點 root &#xff0c; 檢查它是否軸對稱。 示例 1&#xff1a; 輸入&#xff1a;root [1,2,2,3,4,4,3] 輸出&#xff1a;true示例 2&#xff1a; 輸入&#xff1a;root [1,2,2,null,3,null,3] 輸出&#xff1a;false…

Java1.8語言+ springboot +mysql + Thymeleaf 全套家政上門服務平臺app小程序源碼

Java1.8語言 springboot mysql Thymeleaf 全套家政上門服務平臺app小程序源碼 家政系統是一套可以提供上門家政、上門維修、上門洗車、上門搬家等服務為一體的家政平臺解決方案。它能夠與微信對接、擁有用戶端小程序&#xff0c;并提供師傅端app&#xff0c;可以幫助創業者在…

樹的算法基礎知識

什么是樹&#xff1a; 樹是n&#xff08;n>0&#xff09;個結點的有限集。n0時稱為空樹。在任意一棵非空樹中&#xff1a; 有且僅有一個特定的稱為根的結點當n>1時&#xff0c;其余結點可分為m&#xff08;m>0&#xff09;個互不相交的有限集T1、T2、......、Tm&…

ElasticSearch學習筆記之三:Logstash數據分析

第3章 Logstash數據分析 Logstash使用管道方式進行日志的搜集處理和輸出。有點類似*NIX系統的管道命令 xxx | ccc | ddd&#xff0c;xxx執行完了會執行ccc&#xff0c;然后執行ddd。 在logstash中&#xff0c;包括了三個階段: 輸入input --> 處理filter&#xff08;不是必須…

異或炸彈(easy)(牛客小白月賽95)

題目鏈接: D-異或炸彈&#xff08;easy&#xff09;_牛客小白月賽95 (nowcoder.com) 題目&#xff1a; 題目分析&#xff1a; 一看 還以為是二維差分的題呢 到后來才發現是一維差分問題 這里的距離是 曼哈頓距離 dis abs(x - xi) abs(y - yi) 暴力的做法 就是枚舉 n * n 個…

word-海報制作

1、確定海報的尺寸大小 2、創建主題顏色 設計-顏色-自定義顏色-柑橘rgb值改變著色1-著色6的顏色 3、將文字添加至文本框&#xff0c;更改字體顏色、大小和格式 4、添加背景水印&#xff1a;插入-形狀-文本框 5、組合全部元素 圖片素材網址&#xff1a;

Power BI前端設計:深度探索與實戰技巧

Power BI前端設計&#xff1a;深度探索與實戰技巧 Power BI作為一款強大的商業智能工具&#xff0c;其前端設計對于用戶體驗和數據可視化效果至關重要。本文將深入探討Power BI前端設計的四個關鍵方面、五個實用技巧、六個設計要素以及七個注意事項&#xff0c;助您提升Power …

學習分享-如何避免 Apache ShardingSphere 中的笛卡爾積現象

前言 Apache ShardingSphere 是一個開源的分布式數據庫中間件&#xff0c;旨在通過數據分片、分布式事務、分布式治理等技術&#xff0c;提升數據庫系統的性能和可擴展性。然而&#xff0c;最近在使用 ShardingSphere 進行分庫分表并多表查詢時&#xff0c;出現了笛卡爾積現象…

Spark Streaming 概述及入門案例

一、介紹 1. 不同的數據處理 從數據處理的方式&#xff1a; 流式數據處理(Streaming)批量數據處理(Batch) 從數據處理的延遲&#xff1a; 實時數據處理(毫秒級別)離線數據處理(小時或天級別) 2. 簡介 SparkStreaming 是一個準實時(秒或分鐘級別)、微批量的數據處理框架Spa…

在Red Hat Enterprise Linux 9上使用Docker快速安裝并部署RocketMQ

在Red Hat Enterprise Linux 9上快速安裝和部署RocketMQ可以按照以下步驟進行&#xff1a; 1. 安裝Docker 首先&#xff0c;確保Docker已經安裝在你的系統上。 更新系統包并安裝依賴項&#xff1a; sudo yum update -y sudo yum install -y yum-utils device-mapper-persiste…

2024年5月份面試總結

2024年5月份找工作/面試總結&#xff1a; 本人前段時間寫了剛過完年后的一個月內找工作的情況&#xff0c;請查看https://blog.csdn.net/zgaoq/article/details/136236788?spm1001.2014.3001.5501 但是后續寫的總結被和諧了&#xff0c;不知道這篇文章能不能發出來。 1、5月份…

系統架構設計師【第19章】: 大數據架構設計理論與實踐 (核心總結)

文章目錄 19.1 傳統數據處理系統存在的問題19.2 大數據處理系統架構分析19.2.1 大數據處理系統面臨挑戰19.2.2 大數據處理系統架構特征 19.3 Lambda架構19.3.1 Lambda架構對大數據處理系統的理解19.3.2 Lambda架構應用場景19.3.3 Lambda架構介紹19.3.4  Lambda架構的實…

數據庫的換行符到前端不展示了

是這樣的原本數據庫中的數據都是帶有\n換行符的但是頁面卻一直不展示 解決辦法 <el-drawer title"預覽" :visible.sync"drawer" :with-header"false"><div v-for"(item, index) in cardArray" :key"index"><…

如何將 Vue 應用程序部署到 Cloudflare Pages

在現代 Web 開發中&#xff0c;Vue.js 已經成為了一個非常受歡迎的前端框架。它的簡潔、高效和靈活性使得開發人員可以輕松構建出色的用戶界面和交互體驗。而 Cloudflare Pages 提供了一個簡單而強大的方式來托管和部署靜態網站和應用程序。本文將介紹如何將 Vue 應用程序部署到…

Android常見內存泄漏場景總結

一、非靜態內部類造成的內存泄漏 造成原因&#xff1a;非靜態內部類默認會持有外部類的引用&#xff0c;如果內部類的生命周期超過了外部類就會造成內存泄漏。 場景&#xff1a;當Activity銷毀后&#xff0c;由于內部類中存在異步耗時任務還在執行&#xff0c;導致Activity實…

[補題記錄]Leetcode 3.無重復字符的最長子串

傳送門&#xff1a;無重復字符的最長子串 Problem/題意 給一個由英文、數字、符號、空格組成的字符串&#xff0c;找出其中不含有重復字符的最長子串的長度。 比如&#xff1a;abb 包含了重復字符 b&#xff1b;abc 沒有包含重復字符。 注意是子串&#xff0c;不是子序列。 …

內網安全:橫向傳遞攻擊(PTH || PTK || PTT 哈希票據傳遞)

內網安全&#xff1a;橫向傳遞攻擊. 橫向移動就是在拿下對方一臺主機后&#xff0c;以拿下的那臺主機作為跳板&#xff0c;對內網的其他主機再進行后面滲透&#xff0c;利用既有的資源嘗試獲取更多的憑據、更高的權限&#xff0c;一步一步拿下更多的主機&#xff0c;進而達到控…

CodeMirror 創建標簽計算編輯器

在日常開發中對于一些數據計算場景可能會遇到標簽計算的需求&#xff0c;下面關于如何使用CodeMirror實現標簽計算編輯功能。 1&#xff0c;結果圖 2&#xff0c;主體代碼邏輯 大家只需要復制粘貼主要codeMirror使用邏輯即可 <template><el-dialogref"dialogRe…

抖店商家疑惑,自然流量突然下滑,為什么呢?

大家好&#xff0c;我是噴火龍。 很多的抖店商家會遇到一種情況&#xff0c;那就是自己店鋪的流量好好的&#xff0c;不知道怎么的就突然沒流量了&#xff0c;各方面的數據都斷崖式的下降。 為什么會這樣呢&#xff1f;原因有以下幾點&#xff0c;大家可以檢查一下&#xff0…