如标题所示,这个是在java面试中被问频率最高的问题之一,经过我对于网上各种资料的收集和整理,这里会详细说明下二者的区别以及应用场景。
目录
相同点
- 都可以用来存储键值对
- 底层哈希表结构查询速度都很快
- 内部通过单链表解决冲突问题,容量不足会自动增加
- 都实现了Map接口
- 都实现了 Serializable 接口,支持序列化
- 实现了 Cloneable 接口,可以被克隆
不同点
- HashMap线程不安全,效率较高,HashTable是线程安全,效率较低。
- HashMap的键和值都可以是null,HashTable的键和值都不可以是null,会抛出空指针异常
- HashMap继承 AbstractMap 类,HashTable继承Dictionary类
- HashMap使用Iterator遍历,HashTable使用Enumeration遍历
- HashMap和HashTable的初始化方式和扩容方式不同,HashMap的初始大小为16,之后每次扩充容量为原来的两倍,HashTable的初始大小为11,之后每次扩充2n+1
实践测试
键值对是否可以为空测试
可以看到HashTable报错,是因为键值使用了null,单纯的使用key为null或者value为null都是会报错的,所以这个区别是很明显的
public class HashTest { public static void main(String[] args) { Map<String,String> hashMap=new HashMap<>(); hashMap.put(null,"a"); Map<String,String> hashTable=new Hashtable<>(); hashTable.put(null,null); } } //执行结果 Exception in thread "main" java.lang.NullPointerException at java.util.Hashtable.put(Hashtable.java:460) at HashTest.main(HashTest.java:15) Process finished with exit code 1
线程安全观察
在hashMap中是没有的
public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; }
在hashTable的源码发现了方法级同步锁
@SuppressWarnings("unchecked") public synchronized V get(Object key) { Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return (V)e.value; } } return null; }
对于其他的关于继承啥的都可以在源代码中看到
ConcurrentHashMap是干嘛的
虽然jdk提供了HashMap和HashTable但是如何同时满足线程安全和效率高呢,显然这两个都无法满足,所以就诞生了ConcurrentHashMap神器,让我们应用于高并发场景。
该神器采用了分段锁策略,通过把整个Map分成N个Segment(类似HashTable),可以提供相同的线程安全,效率提升N倍,默认提升16倍。
ConcurrentHashMap的优点就是HashMap和HashTable的缺点,当然该神器也是不支持键值为null的
ConcurrentHashMap的出现也意味着HashTable的落幕,所以在以后的项目中,尽量少用HashTable。
应用场景
对于普通场景可以使用hashMap实现,如果是高并发场景建议使用ConcurrentHashMap实现