51.HashMap的實現原理 HashMap的主幹是一個Entry數組。 Entry是HashMap的基本組成單元, 每一個Entry包含一個key-value鍵值對。 HashMap基於hashing原理, 我們通過put()和get()方法儲存和獲取對象。 當我們將鍵值對傳遞給put()方法時 ...
51.HashMap的實現原理
HashMap的主幹是一個Entry數組。 Entry是HashMap的基本組成單元, 每一個Entry包含一個key-value鍵值對。 HashMap基於hashing原理, 我們通過put()和get()方法儲存和獲取對象。 當我們將鍵值對傳遞給put()方法時, 它調用鍵對象的hashCode()方法 來計算hashcode, 讓後找到bucket位置來儲存值對象。 當獲取對象時, 通過鍵對象的equals()方法 找到正確的鍵值對, 然後返回值對象。 HashMap使用鏈表來解決碰撞問題, 當發生碰撞了, 對象將會儲存在鏈表的下一個節點中。 HashMap在每個鏈表節點中儲存鍵值對對象。 當兩個不同的鍵對象的hashcode 相同時會發生什麼? 它們會儲存在同一個bucket位置的鏈表中。 鍵對象的equals()方法用來找到鍵值對。 因為HashMap的好處非常多, 我曾經在電子商務的應用中使用HashMap作為緩存。 因為金融領域非常多的運用Java, 也出於性能的考慮, 我們會經常用到HashMap和ConcurrentHashMap。 HashMap由數組+鏈表組成的, 數組是HashMap的主體, 鏈表則是主要為瞭解決哈希衝突而存在的, 如果定位到的數組位置不含鏈表 當前entry的next指向null, 那麼對於查找, 添加等操作很快, 僅需一次定址即可; 如果定位到的數組包含鏈表, 對於添加操作, 其時間複雜度為O(n), 首先遍歷鏈表, 存在即覆蓋, 否則新增; 對於查找操作來講, 仍需遍歷鏈表, 然後通過key對象的equals方法逐一比對查找。 所以,性能考慮,HashMap中的鏈表出現越少, 性能才會越好。
52.List、Set、Map之間的區別
List、Set是實現了Collection介面的子介面; 而Map是另一個集合介面。 1元素重覆性: List允許有重覆的元素。 任何數量的重覆元素 都可以在不影響現有重覆元素的值 及其索引的情況下插入到List集合中; Set集合不允許元素重覆。 Set以及所有實現了Set介面的類 都不允許重覆值的插入, 若多次插入同一個元素時, 在該集合中只顯示一個; Map以鍵值對的形式對元素進行存儲。 Map不允許有重覆鍵, 但允許有不同鍵對應的重覆的值; 2.元素的有序性: List及其所有實現類保持了 每個元素的插入順序; Set中的元素都是無序的; 但是某些Set的實現類 以某種殊形式對其中的元素進行排序, 如:LinkedHashSet按照元素的 插入順序進行排序; Map跟Set一樣對元素進行無序存儲, 但其某些實現類對元素進行了排序。 如:TreeMap根據鍵對其中的元素進行升序排序; 3.元素是否為空值: 1.List允許任意數量的空值; 2.Set最多允許一個空值的出現; 當向Set集合中添加多個null值時, 在該Set集合中只會顯示一個null元素 3.Map只允許出現一個空鍵, 但允許出現任意數量的空值; --------------------------------- 總結: List中的元素: 有序、可重覆、可為空; Set中的元素: 無序、不重覆、只有一個空元素; Map中的元素: 無序、鍵不重,值可重、可一個空鍵、多可空值;
53.HashMap 的長度為什麼是2的冪次方
1.減小哈希衝突概率 假如當前Entry數組長度為len, 插入節點時, 需要對key的hashcode進行二次哈希, 然後跟len-1相與 得到的值一定小於len,避免數組越界 如果len是2的N次方, 那麼len-1的後N位二進位一定是全1 假設有兩個key, 他們的hashcode不同, 分別為code1和code2 code1和code2分別與一個後N位全1的二進位相與, 結果一定也不同 但是,如果code1和code2分別 與一個後N位非全1的二進位相與, 結果有可能相同 也就是說, 如果len是2^N, 不同hashcode的key 計算出來的數組下標一定不同; 否則, 不同hashcode的key 計算出來的數組下標一定相同。 所以HashMap長度為全1, 可以減小哈希衝突概率。 ---------------------- 2.提高計算下標的效率 如果len的二進位後n位非全1, 與len-1相與時, 0與1相與需要取反。 如果len的二進位後n位全1, 完全不需要取反。 如果len為2^N, 那麼與len-1相與, 跟取餘len等價, 而與運算效率高於取餘。 如果len不是2^N, 與len-1相與, 跟取餘len不等價。
54.集合框架中的泛型有什麼優點?
首先, 瞭解一下Java關於泛型的概念。 泛型,在C++中被稱為模板, 就是一種抽象的編程方式。 當我們定義類和方法的時候, 可以用一種通用的方式進行定義, 而不必寫出具體的類, 這些未知的東西會在真正使用的時候在確定。 對於集合類來說, 它們可以存放各種類型的元素。 如果在存放之前, 就能確定元素的類型, 那麼就可以更加直觀, 也讓代碼更加簡潔。 說明: java的泛型是停留在編譯層的, 也就是說JVM在對待泛型數據的時候, 依然會把它們看成是Object類型, 只不過在使用這些元素的時候, JVM會自動幫助我們進行相應的類型轉換。 總結: 集合使用泛型之後, 可以達到元素類型明確的目的, 避免了手動類型轉換的過程, 同時,也讓我們更加明確 容器保存的是什麼類型的數據。
55.我們能否使用任何類作為Map的key?
1、可以 但是做為key的數據有如下要求: 2、首先, 要求明確一點Map集合存儲數據的 主要目的是為了查找 而List集合是為了輸出 3、既然是查找那麼就要涉及到對象比較 我們說瞭如果要進行對象比較 就必須覆寫Object類中的equals()、hasCode() 至少覆寫equals()方法 簡單理解: 自己定義的類如果要想實現對象比較 就必須至少覆寫equals()方法 4、或則這麼說只要是自己定義的類 要想將其作為key 就必須覆寫equals()方法 5、實際工作中 key的類型一定是String型 (95%通用) 其餘的5%是沒事找事的 6、按標準開發、你會感到事半功倍, 不要沒事給自己找事, 當然求知精神是值得肯定的。
56.Map介面提供了哪些不同的集合視圖?
Map介面提供了三個集合視圖: 1.Set keyset(): 返回map中包含的所有key的一個Set視圖。 集合是受map支持的, map的變化會在集合中反映出來, 反之亦然。 當一個迭代器正在遍歷一個集合時, 若map被修改了(除迭代器自身的移除操作以外), 迭代器的結果會變為未定義。 集合支持通過 Iterator的Remove、 Set.remove、 removeAll、 retainAll和clear操作進行元素移除, 從map中移除對應的映射。 它不支持add和addAll操作。 2.Collection values(): 返回一個map中包含的 所有value的一個Collection視圖。 這個collection受map支持的, map的變化會在collection中反映出來, 反之亦然。 當一個迭代器正在遍歷一個collection時, 若map被修改了(除迭代器自身的移除操作以外), 迭代器的結果會變為未定義。 集合支持通過 Iterator的Remove、 Set.remove、 removeAll、 retainAll和clear操作進行元素移除, 從map中移除對應的映射。 它不支持add和addAll操作。 3.Set> entrySet(): 返回一個map鐘包含的 所有映射的一個集合視圖。 這個集合受map支持的, map的變化會在collection中反映出來, 反之亦然。 當一個迭代器正在遍歷一個集合時, 若map被修改了 除迭代器自身的移除操作, 以及對迭代器返回的entry進行setValue外, 迭代器的結果會變為未定義。 集合支持通過 Iterator的Remove、 Set.remove、 removeAll、 retainAll和clear操作進行元素移除, 從map中移除對應的映射。 它不支持add和addAll操作。
57.哪些集合類是線程安全的?
在集合框架中,有些類是線程安全的,
這些都是jdk1.1中的出現的。
在jdk1.2之後,
就出現許許多多非線程安全的類。
下麵是這些線程安全的同步的類:
vector:
就比arraylist多了個同步化機制(線程安全),
因為效率較低,
現在已經不太建議使用。
在web應用中,
特別是前臺頁面,
往往效率(頁面響應速度)是優先考慮的。
statck:堆棧類,先進後出
hashtable:就比hashmap多了個線程安全
enumeration:枚舉,相當於迭代器
除了這些之外,
其他的都是非線程安全的類和介面。
線程安全的類其方法是同步的,
每次只能一個訪問。
是重量級對象,
效率較低。
58.隊列和棧是什麼,列出它們的區別?
隊列(Queue): 是限定只能在表的一端進行 插入和在另一端進行刪除操作的線性表; 棧(Stack): 是限定只能在表的一端 進行插入和刪除操作的線性表。 區別如下: 一、規則不同 1. 隊列:先進先出(First In First Out)FIFO 2. 棧:先進後出(First In Last Out )FILO 二、對插入和刪除操作的限定不同 1. 隊列:只能在表的一端進行插入, 併在表的另一端進行刪除; 2. 棧:只能在表的一端插入和刪除。 三、遍曆數據速度不同 1. 隊列:基於地址指針進行遍歷, 而且可以從頭部或者尾部進行遍歷, 但不能同時遍歷, 無需開闢空間, 因為在遍歷的過程中不影響數據結構, 所以遍歷速度要快; 2. 棧:只能從頂部取數據, 也就是說最先進入棧底的, 需要遍歷整個棧才能取出來, 而且在遍曆數據的同時需要 為數據開闢臨時空間, 保持數據在遍歷前的一致性。
59.哪一個List實現了最快插入?
LinkedList和ArrayList
是另個不同變數列表的實現。
ArrayList的優勢在於動態的增長數組,
非常適合初始時總長度未知的情況下使用。
LinkedList的優勢在於在中間位置插入和刪除操作,
速度是最快的。
LinkedList實現了List介面,
允許null元素。
此外LinkedList提供額外的
get,remove,insert方法
在LinkedList的首部或尾部。
這些操作使LinkedList可被
用作堆棧(stack),
隊列(queue)
或雙向隊列(deque)。
ArrayList實現了可變大小的數組。
它允許所有元素,
包括null。
每個ArrayList實例都有一個容量(Capacity),
即用於存儲元素的數組的大小。
這個容量可隨著不斷添加新元素而自動增加,
但是增長演算法並沒有定義。
當需要插入大量元素時,
在插入前可以調用ensureCapacity方法來
增加ArrayList的容量以提高插入效率。
60.什麼時候使用ConcurrentHashMap?
快速失敗的Java迭代器
可能會引發ConcurrentModifcationException
在底層集合迭代過程中被修改。
故障安全作為發生在實例中的一個副本
迭代是不會拋出任何異常的。
快速失敗的故障安全範例定義了
當遭遇故障時系統是如何反應的。
例如,用於失敗的快速迭代器ArrayList
和用於故障安全的迭代器ConcurrentHashMap。
ConcurrentHashMap被作為
故障安全迭代器的一個實例,
它允許完整的併發檢索和更新。
當有大量的併發更新時,
ConcurrentHashMap此時可以被使用。
這非常類似於Hashtable,
但ConcurrentHashMap不鎖定
整個表來提供併發,
所以從這點上ConcurrentHashMap的性能
似乎更好一些。
所以當有大量更新時
ConcurrentHashMap應該被使用。