JDK1.7

HashMap的实现 数组+ 链表

图示:

image.png

用法:

  1. Map<String, String> hashMap = new HashMap<>();
  2. hashMap.put("key ","value");

构造过程:

  1. 初始化一个数组 大小为0;
  2. put 时会初始化,默认大小为16

可以指定数组的长度

  1. Map<String, String> hashMap = new HashMap<>(7);
  2. 可以指定大小,但实际的大小是2的次幂

put的过程 采用头插法

  1. 先根据key 的 hashCode 确定数组的下标
  2. 替换原来的链表的头节点
  3. 把新的头的节点的引用指向数组

如果key = null ,会放在第一个节点位置

获取下标的方法

  1. static int indexFor(int h, int length) {
  2. // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
  3. return h & (length-1);
  4. }

获取一个数的最近的一个2次幂的值的算法

  1. /**
  2. * Returns a power of two size for the given target capacity.
  3. */
  4. static final int tableSizeFor(int cap) {
  5. int n = cap - 1;
  6. n |= n >>> 1;
  7. n |= n >>> 2;
  8. n |= n >>> 4;
  9. n |= n >>> 8;
  10. n |= n >>> 16;
  11. return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
  12. }

思路:
想把 0001 ** 后面变成1
再把 1右 移一位
在减去 后面的1 就全减为0 了
结果就是事 0001 0000了

put 的主要逻辑

  1. put(key ,value)
  2. int hashCode = key.hashCode();
  3. int index = hashCode & (数组长度-1)
  4. 遍历index位置的链表,如果存在相同就进行value的覆盖 ,并发挥旧的value
  5. 讲key value 封装成Entry
  6. 将节点插在index位置上的链表头部
  7. 把链表头节点移动到数组上

可能会带来 循环链表的问题

在扩容时,多个线程操作链表
就造成数据不一样,从而有可能会形成循环列表

扩容过程

1:声明一个新的大小的数据
2:遍历旧数组,就链表
3:根据新数据的大小,扩展因子重新hash值
4:将就元素引用指向新的数组

initHashSeedAsNeeded 初始一个hash种子,
为了让元素更加的分散

并发修改异常

modCount++
在put,remove 时会修改这个modCount
判断是否有过修改,实现快速失败

HashTable:

put 的时候加锁
对这个table加锁

  1. public synchronized V put(K key, V value) {
  2. // Make sure the value is not null
  3. if (value == null) {
  4. throw new NullPointerException();
  5. }
  6. // Makes sure the key is not already in the hashtable.
  7. Entry<?,?> tab[] = table;
  8. int hash = key.hashCode();
  9. int index = (hash & 0x7FFFFFFF) % tab.length;
  10. @SuppressWarnings("unchecked")
  11. Entry<K,V> entry = (Entry<K,V>)tab[index];
  12. for(; entry != null ; entry = entry.next) {
  13. if ((entry.hash == hash) && entry.key.equals(key)) {
  14. V old = entry.value;
  15. entry.value = value;
  16. return old;
  17. }
  18. }
  19. addEntry(hash, key, value, index);
  20. return null;
  21. }

ConcurrentHashMap

结构:
Segment [] table;
Entry [] tab;

image.png
ssize : segment数组的带下,与并发级别有关
最小是2

先扩容 HashEntry [] 的这个数组