1. 程式人生 > >Java每天10道面試題,跟我走,offer有!(六)

Java每天10道面試題,跟我走,offer有!(六)

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應該被使用。