HashMap原理

30 阅读2分钟

特点

  • Map接口的常用实现类:HashMap、Hashtable和Properties
  • HashMap 是以 key-value 对的方式来存储数据(HashMap$Node类型)
  • 键不能重复,但是值可以重复,允许使用null键和null值
  • 如果添加相同的key,则会覆盖原来 key-value ,等同于修改(key不会替换,value会替换)
  • 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的
  • HashMap线程不安全的

关于HashMap的线程安全

方法示例原理性能
HashTableMap<String, Object> map = new Hashtable<>();synchronized修饰get/put方法。方法级阻塞,只能同时一个线程操作get或put很差
Collections.synchronizedMapMap<String, Object> map =Collections.synchronizedMap(new HashMap<String, Object>());内部有个mutex对象,对它加锁很差
JUC中的ConcurrentHashMapMap<String, Object> map = new ConcurrentHashMap<>();每次只给一个桶(数组项)加锁很好

扩容机制(具体源码看HashSet)

  • HashMap底层维护了Node类型的数组table,默认为null
  • 当创建对象时,将加载因子(loadfactor)初始化为0.75
  • 当添加key-value时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没有元素则直接添加;如果该索引处有元素,则继续判断该元素的key和准备加入的key是否相等,如果相等,则直接替换value;如果不相等,则需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容
  • 第一次添加,则需要扩容table容量为16,临界值(threshold)为12(16*0.75)
  • 以后再扩容,则需要扩容table容量为原来的2倍(32),临界值为原来的2倍,即24,依次类推
  • 在java8中,如果一条链表的元素个数 超过 TREEIFY_THRESHOLD(默认是8),并且 table 的大小 >= MIN_TREEIFY_CAPACITY(默认是64),就会进行树化(红黑树)