HashMap VS HashTable
- hashtable is synchronized, whereas HashMap is not.
- This makes HashMap better for non-threaded applications, as unsynchronized OBjects typically perform better than synchronized ones.
- hashMap 支持null key 和 null value
- 本质上是因为hashMap将 null 的hashCode值定为了0。
- 实现原理:
- 数据结构:HashMap和HashTable都使用哈希表来存储键值对,在数据结构上是基本相同的。
- 都创建了一个继承自 Map.Entry 的私有内部类 Entry,每一个Entry对象表示存储在哈希表中的一个键值对。
- 算法:
- 将给定的key映射到确定的hash桶,即数组位置。
- 在桶内键值对多到一定程度时,扩充哈希表(数组)。
- 哈希表大小的选择:
- hashTable会尽量使用素数、奇数。[素数会使得简单的取模哈希结果更加均匀] --> 冲突更少(原理上)
- hashMap则总是使用2的幂作为哈希表的大小。[取模时,如果模数是2的倍数,那么可以直接使用位运算来得到结果] --> 哈希计算效率更高
--> 事实上,hashMap的这一做法引入了哈希分布不均匀的问题,所以HashMap为了解决这一问题,又对hash算法做了一些改进。(貌似是通过位运算来打散数据的,具体略)
- 哈希表大小的选择:
- 数据结构:HashMap和HashTable都使用哈希表来存储键值对,在数据结构上是基本相同的。
- 线程安全性
- HashTable是怎么做到线程安全的呢?--> 对public方法都使用了synchronized关键字。
- Usage:实际上HashTable已经被淘汰了。如果需要线程安全,请使用ConcurrentHashMap。
ConcurrentHashMap
- 既然上面提到了,我们就来看一下ConcurrentHashMap:高并发性的hashMap。
- 代码结构:
- ConcurrentHashMap类中包含两个静态内部类:
- HashEntry:hash表中的每个桶是由若干个HashEntry对象链接起来的链表。
- Segment:用来充当锁的角色,每个Segment对象守护整个散列映射表的若干个桶。
- 所以一个ConcurrentHashMap是由 1. table: 若干个由若干个HashEntry对象组成的链表 组成的hash表; 2. 若干个segment对象组成的数据。
- ConcurrentHashMap类中包含两个静态内部类:
- Segment:继承自ReentrantLock类。
- 每个Segment对象中包含一个counter,用来表示本segment中包含的HashEntry对象的总数。 --> 避免出现“热点问题”。
- 那么ConcurrentHashMap的核心就变成了:如何用分离锁实现多个线程间的并发写操作。
- 每次加锁都是针对(key的hash值对应的)某个具体的Segment,锁定的不是整个ConcurrentHashMap
- 理想状态下可以 x(segment个数) 个线程执行并发写操作。
- 接下来分析下线程写入的两种情况:
- 对散列表做非结构性修改,i.e.只是修改某个HashEntry的value。由于HashEntry的value被声明为volatile,写操作是能立即被后续不加锁的读线程“看到”的。
- 对ConcurrentHashMap做结构性修改,实质上是对某个桶指向的链表进行修改。如果能够保证:在读线程遍历一个链表期间,写线程对这个链表所做的结构性修改不影响遍历,那么就能保证读/写之间的安全并发。
- clear:把某个桶置空。只是简单的将桶不再引用原链表,并不会影响之前的读。
- remove:remove要先根据hashCode找到具体的链表,然后遍历该链表。因为HashEntry内的key,hash,next指针都是final不可变的,所以删除一个节点需要将“所有处于待删除节点之后的节点原样保留在链表中, 所有处于待删除节点之前的节点被克隆到新链表中”。整个过程是不会影响之前的遍历的。
e.g: 删除C节点![]()
- 总结:在实际应用中,hashMap一般应用场景都是多读少改的。因此ConcurrentHashMap通过HashEntry对象的不变形和用volatile型变量协调线程之间的内存可见性,使得大多数时候,读操作不需要加锁就能获得正确值。这个特性使得ConcurrentHashMap的并发性在分离锁的基础上又有了进一步的提高。
- ConcurrentHashMap的高并发性主要来自三个方面:
- 用分离锁实现多个线程间更深层次的共享访问
- 用HashEntry对象的不变性来降低执行读操作的线程在遍历期间对锁的需求。
- 通过对同一个volatile变量的读写访问,协调不同线程间读写操作的内存可见性。
Iterator
- iterator是一种设计模式,它是一个对象,可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器的真正作用在于:能够将遍历序列的操作与序列底层的结构分离。
Implementation
- ArrayList的内部类 Itr (implements Iterator<E>)
- fields:
- int cursor = 0; // index of element to be returned by subsequent call to next
- int lastRet = -1; // index of element returned by most recent call to next or previous. Reset to -1 if this element is deleted by a call to remove
- int expectedModCount = modCount; // The modCount value that the iterator believes that the backing List should have. If this expectation is violated, the itertor has detected concurrent modification.
- The list-iterator is fail-fast: if the list is structurally modified at any time after the Iterator is created, in any way except through the list-iterator's own remove or add, the list-iterator will throw a ConcurrentModificationException. [这就是通过expectedModCount做到的]
- fields:
- LinkedList的内部类 ListItr (implements Iterator<E>)
- fields:
- Node<E> lastReturned;
- Node<E> next;
- int nextIndex;
- int expectedModCount = modCount;
- fields:
FYI
满地都是六便士,她却抬头看见了月亮。

浙公网安备 33010602011771号