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