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