目录
一、HashMap
HashMap是Java中的一个数据结构,它实现了Map接口。HashMap通过一个哈希表来存储key-value键值对,并且支持高效地添加、删除和查找操作。在HashMap中,key和value可以为任何非空对象,而且可以为null。HashMap的特点包括:快速访问,高效的插入和删除,非线程安全。HashMap也是Java中最常用的集合之一。
- HashMap 的键值可以为null (当key为空时,哈希会被赋值为0)
- HashMap 的默认初始容量是16, 最大容量是2^30;
- HashMap 使用的数据结构是 数组 + 链表 + 红黑树
- HashMap 效率非常高,但线程不安全
二、HashTable
HashTable是Java中的一个数据结构,也实现了Map接口。它与HashMap相似,都是通过哈希表来存储键值对。然而,HashTable是线程安全的,它的各种操作都是同步的,可以避免并发操作带来的问题。相应地,HashTable的性能可能会受到影响,因为同步机制会引入一定的开销。HashTable的特点包括:线程安全、支持键值对的插入、删除和查找、key和value都不能为null。然而,由于HashTable的同步机制,建议在只有单线程访问的情况下使用HashMap。
- HashTable 的键值不能为null
- HashTable 虽然线程安全,但只是简单得用 synchronized 给所有方法加锁,相当于是对this加锁,也就是对整个HashTable对象进行加锁(非常无脑)
在多线程环境下,这种“无脑”的加锁方式是非常低效的:
- 多个线程访问同一个Hashtable对象时,无论做什么操作,都会产生锁冲突;
- 如果某个线程触发了扩容机制,那么就会由这个线程完成整个扩容过程,如果元素过多,效率则非常低,其它线程阻塞等待的时间也会更长。
- Java官方已经不推荐使用Hashtable了~
- 不涉及线程安全问题时使用HashMap,如果要保证线程安全就使用ConcurrentHashMap
三、ConcurrentHashMap
(1)锁粒度的控制
ConcurrentHashMap不是锁整个对象,而是使用多把锁,对每个链表(哈希桶)都进行加锁,只有当两个线程同时访问同一个链表时,才会产生锁竞争,因此极大地降低了锁冲突的概率。
(2)ConcurrentHashMap 只给写操作加锁,读操作没加锁
ConcurrentHashMap只对写操作进行加锁,读操作没有加锁,此时会有三种情况:
- 两个线程同时修改一个哈希桶时才会产生锁冲突;
- 两个线程同时读数据,不会有锁冲突;
- 一个线程修改,一个线程读,也没有锁冲突。
- 第三种情况可能会有线程不安全问题,这和我们写的代码有关,但是ConcurrentHashMap中的读操作使用了Volatile,来保证读到的数据不是修改了一半的数据。
(3)充分利用到了CAS的特性
ConcurrentHashMap充分利用了CAS的特性,避免出现重量级锁的情况。
(4)扩容操作特殊优化
Hashtable的扩容机制是,创建一个更大的新数组,然后由一个线程一次性把旧数组中的元素搬运到新数组。因为如果数据量很大的时候,创建新数组将数据全部搬运过去,那么开销就会很大,所以为了解决这个问题 进行了这个优化
而ConcurrentHashMap则是让新数组和旧数组同时存在一段时间,在这期间,后续每个操作ConcurrentHashMap的线程都会负责搬运旧数组的一部分元素到新数组,直到搬运完旧数组的最后一个元素时,再把旧数组删除~
搬运期间,插入元素时直接插入到新数组中;查询元素时,新数组和旧数组一起查。
总结
HachMap和HashTable的区别
- HashMap线程不安全,HashTable线程安全。
- 包含的contains方法不同,HashMap是没有contains方法的。
- Hashmap是允许key和value为null值的。
- 计算hash值方式不同。
- .扩容方式不同。
- 解决hash冲突方式不同。
- HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类。
- 对外提供的接口不同,Hashtable比HashMap多提供了elements() 和contains() 两个方法。