1. 程式人生 > >HashMap面試相關

HashMap面試相關

HashMap面試相關整理

問題1:HashMap的基本原理和Hash衝突。

  • 結構:鍵值對 Key,value 結構。陣列+連結串列
  • 初始容量:static final int DEFAULT_INITIAL_CAPACITY = 16;
  • 最大值 static final int MAXIMUM_CAPACITY = 1 << 30;
  • 負載因子: static final float DEFAULT_LOAD_FACTOR = 0.75f;
  • 父類: 繼承自AbstractMap<K,V> 實現介面Map<K,V>
  • HashMap執行緒不安全,ConcurrentHashMap(Segment分段鎖)執行緒安全、效率高、HashTable(synchronized)執行緒安全、效率低。
  • 允許NULL Key和Value
public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
  • 封裝一個實體類
package com.javaee.collections.map;
/**
* 
* @Title: MapElement  
* @Description: TODO(檢測Hash衝突)  
* @author X-Dragon  
* @version V1.0  
*
*/
public class MapElement { private int id; private String name; public MapElement() { super(); } public MapElement(int id, String name) { super(); this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this
.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } // @Override // public int hashCode() { // final int prime = 31; // int result = 1; // result = prime * result + id; // result = prime * result + ((name == null) ? 0 : name.hashCode()); // return result; // } /** * * @Title: hashCode * @Description: TODO(返回值都是1,用於測試Hash衝突) * @return * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return 1; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; MapElement other = (MapElement) obj; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } @Override public String toString() { return "MapElement [id=" + id + ", name=" + name + "]"; } }
  • 測試類
package com.javaee.collections.map;

import java.util.HashMap;
import java.util.Map;

public class HashMapTest {

   public static void main(String[] args) {
   	// TODO Auto-generated method stub
   	HashMap<MapElement, String> map = new HashMap<MapElement, String>();
   	MapElement mapElement1 = new MapElement(1, "xielong1");//A 放入
   	MapElement mapElement2 = new MapElement(2, "xielong2");//B 放入
   	MapElement mapElement3 = new MapElement(2, "xielong2");//C 不放入
   	MapElement mapElement4 = new MapElement(2, "xielong4");//D 放入
   	if(mapElement1.hashCode()==mapElement2.hashCode()){
   		System.out.println("hashCode相等:");
   	}
   	map.put(mapElement1, mapElement1.getName());
   	map.put(mapElement2, mapElement2.getName());
   	map.put(mapElement3, mapElement3.getName());
   	map.put(mapElement4, mapElement4.getName());
   	map.put(null, null);//放入NULL
//		map.put(null);//語法報錯
   	System.out.println(map.size());//包含NULL4個 不算NULL 三個
   	for(Map.Entry<MapElement, String> entry:map.entrySet()){//遍歷元素
   		System.out.println("Key:"+entry.getKey()+"Value:"+entry.getValue());
   	}
   }

}
  • 結論:

  • put方法

    • 判斷是否可以放入MAP集合。首先判斷key的hashCode()是否衝突,不衝突直接放入。如果hashCode()一致,然後判斷equals()方法是否相等。如果equals()不相等,就根據Key的hashCode()找到對應的位置放入KEY衝突的資料。
    • 當建立 HashMap 時,有一個預設的負載因子(load factor),其預設值為 0.75,這是時間和空間成本上一種折衷:增大負載因子可以減少 Hash 表(就是那個 Entry 陣列)所佔用的記憶體空間,但會增加查詢資料的時間開銷,而查詢是最頻繁的的操作(HashMap 的 get() 與 put() 方法都要用到查詢);減小負載因子會提高資料查詢的效能,但會增加 Hash 表所佔用的記憶體空間。
    • equals()和hashCode()方法都要一起重寫。
  • get方法

    • 根據Key找到對應的hashCode()方法找到bucket位置,然後獲取值物件。

    • 如果兩個鍵的hashcode相同,當我們呼叫get()方法找到bucket位置之後,會呼叫keys.equals()方法去找到連結串列中正確的節點,最終找到要找的值物件。

    • 許多情況下,面試者會在這個環節中出錯,因為他們混淆了hashCode()和equals()方法。因為在此之前hashCode()屢屢出現,而equals()方法僅僅在獲取值物件的時候才出現。一些優秀的開發者會指出使用不可變的、宣告作final的物件,並且採用合適的equals()和hashCode()方法的話,將會減少碰撞的發生,提高效率。不可變性使得能夠快取不同鍵的hashcode,這將提高整個獲取物件的速度,使用String,Interger這樣的wrapper類作為鍵是非常好的選擇。

  • HashMap怎麼進行動態擴容

    • 擴容的方式是新建一個newTab,是oldTab的2倍。遍歷oldTab,將oldTab賦值進對應位置的newTab。與ArrayList中的擴容邏輯基本一致,只不過ArrayList是當前容量+(當前容量>>1)。
    • JDK1.8使用了紅黑樹(自平衡二叉查詢樹)TreeMap相對複雜。

問題2:HashMap、HashTable、HashSet區別

  • A:HashSet是set的一個實現類,hashMap是Map的一個實現類,同時hashMap是hashTable的替代品。
  • HashMap執行緒不安全,允許空KEY和空VALUE,不允許放入一整個NULL,CurrentHashMap執行緒安全、效率高、HashTable執行緒安全、效率低、不允許放入任何空值。
  • HashMap和Hashtable的區別
    • 1 繼承和實現方式不同
    • HashMap 繼承於AbstractMap,實現了Map、Cloneable、java.io.Serializable介面。
      Hashtable 繼承於Dictionary,實現了Map、Cloneable、java.io.Serializable介面。
      https://www.cnblogs.com/skywang12345/p/3311126.html
    • 2 HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因為contains方法容易讓人引起誤解。
    • 3 Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map interface的一個實現。
      最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多個執行緒訪問Hashtable時,不需要自己為它的方法實現同步,而HashMap 就必須為之提供外同步。
      Hashtable和HashMap採用的hash/rehash演算法都大概一樣,所以效能不會有很大的差異。
    • 就HashMap與HashTable主要從三方面來說。
      一.歷史原因:Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map介面的一個實現
      二.同步性:Hashtable是執行緒安全的,也就是說是同步的,而HashMap是執行緒序不安全的,不是同步的
      三.值:只有HashMap可以讓你將空值作為一個表的條目的key或value