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