6.HashMap的实现原理
HashMap内部维护了一个数组,数组的每个元素称为一个桶,当我们向HashMap中放入一个键值对时,首先会根据key的哈希值计算出这个键值对在数组中的索引位置。当不同的key通过哈希函数计算得到相同的索引位置时,这些键值对会以链表的形式存储在同一个桶中,在java8中,如果链表长度达到一定阈值(默认为8),并且数组长度大于64,这个链表会被转换为红黑树,提高查找效率。
7.哈希冲突的解决方法
1.线性探测法
当发生哈希冲突时,线性探测法会按照一定的顺序,在哈希表中查找下一个空闲的位置来存储冲突的元素;比如哈希函数计算的初始位置是3,如果位置被占用,就会尝试4,5等位置,以此类推直到找到下一个空闲位置。
2.二次探测法
它不是按照顺序以此探测下一个位置,而是按照一个二次函数的方式来探测空闲位置。但是可能出现探测序列的周期性,导致某些位置无法被探测到。
3.双重哈希法
双重哈希法使用两个不同的哈希函数,当发生冲突时,首先使用第一个哈希函数计算出初始位置x,如果该位置被占用,就用第二个哈希函数计算出一个偏移量d,然后按照x+d,x+2d的顺序来探测空闲位置。但是设计两个合适哈希函数比较复杂,需要确保第二个哈希函数产生的偏移量能够遍历整个哈希表,避免出现无法探测的空洞。
4.链地址法
在一个简单的哈希表实现中,有一个数组作为哈希桶,每个桶指向一个链表。当有新元素插入且发生冲突时,就将新元素插入到对应桶所连接的链表的头部或者尾部(取决于具体的实现)。在查找元素时,首先通过哈希函数找到对应的桶,然后在链表中逐个比较元素,直到找到目标元素或者遍历完整个链表确定不存在目标元素。
5.再哈希法
当发生哈希冲突时,使用另一个哈希函数重新计算哈希值,直到找到一个空闲的存储位置。
8.HashMap和Hashtable的区别
1.线程安全性
- HashMap:非线程安全,在多线程环境下,如果多个线程同时对HashMap进行插入,删除或者修改操作,可能导致数据不一致,死循环等问题。比如在扩容过程中,如果多个线程同时触发扩容操作,可能会导致链表形成环,进而在后续的遍历操作中出现死循环。
- Hashtable:线程安全,方法基本都被synchronized关键字修饰。
2.null值处理
- HashMap:允许key为null,但只能有一个key为null。同时允许value为空,这使得在某些应用场景下,如缓存系统中,使用 HashMap 更加灵活,因为可以方便地用 null 值来表示某些特殊状态,比如缓存未命中。
- Hashtable:不允许key或者value为null,会抛出异常。
3.迭代器一致性
- HashMap:在迭代器创建后,如果其他线程对 HashMap 进行了结构上的修改(如插入、删除元素),迭代器可能会抛出
ConcurrentModificationException
异常。这是因为 HashMap 内部有一个modCount
变量,用于记录结构修改的次数,当迭代器在运行过程中发现modCount
与预期的值不一致时,就会抛出该异常。 - Hashtable:在迭代器创建后,其他线程对Hashtable进行修改时,不会抛出异常,因为它的迭代器是弱一致性的。
4.初始容量和扩容机制
- HashMap:默认初始容量为16,负载因子为0.75.当元素个数超过容量*负载因子的适合,会触发扩容操作,新的容量为原来的两倍。
- Hashtable:默认初始容量为11,负载因子为0.75.扩容时,新的容量是原来的二倍加一。
9.HashMap 和 ConcurrentHashMap 的区别
1.线程安全
ConcurrentHashMap:是线程安全,采用了多种复杂技术来确保在多线程环境下的正确操作,在java7和之前的版本中,它采用了分段锁的机制:将数据分成多个段,每个段类似一个独立的小哈希表,不同段之间的操作可以并发进行,只有在对同一段进行操作时才需要获取到锁,大大提高了并发性能。在java8和以后的版本,它采用了更加细粒度的Node节点所,并且在一些操作大部分情况再时无锁的,进一步提高了并发性能。
HashMap:是非线程安全的。在多线程环境下,当多个线程同时对 HashMap 进行写操作(如put
、remove
)时,数据可能会出现不一致的情况。
2.数据结构
- HashMap:内部主要是由数组和链表(当链表长度超过一定阈值时会转换为红黑树)构成。它通过计算键(key)的哈希值来确定元素在数组中的位置,当出现哈希冲突时,将元素以链表(或红黑树)的形式存储在相应位置。
- ConcurrentHashMap:在java7之前,主要由Segment和HashEntry数组构成,segment是一种可重入锁,主要作用是将哈希表分成多段,每个segment是一个独立的哈希表,管理着一个HashEntry数组。hashentry是实际存储键值对的节点。