1、HashMap 实现了Map集合接口,它允许key, value的值为null。他不保证线程同步和顺序,它可能会在修改过程中改变顺序。
2、get, put 方法可以对集合进行添加和或者,在初始化过程中需要设置容器大小(capacity),如果没有设置容器大小默认为16,在resize过程中会重建hash,严重影响性能 。
3、HashMap有两个重要的参数:初始容量(initial capacity) 和 负载因子 (load factor);初始容量,是在创建容器的时候初始化的容器大小,负载因子,在容量自动增加之前会获取负载因子这个参数,当容器内的元素大于 当前容量* 负载因子 的时候HASH表会重新刷新(内部数据重新构建)。
4、负载因子,的默认值为0.75是一个比较合理的值,对于时间和空间的成本的权衡,较高会降低空间开销,但是增减查找成本。初始容量,如果不设置默认值16,如果设置为 最大元素数量 / 负载因子 。那么就不会发生 rehash。
5、输入元素比较多的情况,需要设置一个比较大的 初始容量,避免在写入过程中拓容降低性能。如果有很多相同的hashcode的元素会降低性能,为了降低这个影响,如果支持关键字:java.lang.Comparable,可以对它做一次排序。
6、HashMap不是线程俺钻的,如果需要对它做线程同步必须在访问他之前多一个线程同步加锁,在删除和添加操作的时候都需要加同步锁,当修改的时候不需要,应为修改不会改变容器结构
7、一般在多线程使用map的时候,都是使用map的线程安全包装类,可以通过:Map m = Collections.synchronizedMap(new HashMap(...)); 来创建一个线程安全的Map集合在创建的时候可以保证线程安全。
8、当并发迭代和结构修改时,除了使用的迭代器本身的remove方法之外,否则会抛出一个异常:ConcurrentModificationException,因此面对并发场景下的map迭代就会抛出异常 。 fail-fast 的表现不是能够保证一定发生,只是尽量抛出异常,因此依赖此异常的程序是错误的做法.
9、1.8 对哈希捧着黄后的拉链算法进行了优化,当拉链上entity数量太多(默认8个)将链表转换为红黑树。
总结:hashmap 的结构是通过 key 转换为 hashcode 作为数组的 下标,如果下标重复通过链表链解决,当链表的长度大于阀值得时候转换为红黑树来减少查询的次数。
10、关于hashmap 的排序,两个元素的排序通过 Comparable 来实现,如果没有实现 Comparable 类将通过反射来处理,这样会增加 bucketed 的性能来降低性能。
11、集合的容量是通过2倍拓容,bucket 容量大于8的时候会变型为 红黑树,当容量小于6的时候会转变为链表。负载因子的默认值为 0.75
12、成员属性解释:
// 默认容量
DEFAULT_INITIAL_CAPACITY = 16
// 最大容量
MAXIMUM_CAPACITY = 1 << 30
// 负载因子
DEFAULT_LOAD_FACTOR = 0.75f
// 链表转换为红黑树阀值
TREEIFY_THRESHOLD = 8
// 红黑树转换链表阀值
UNTREEIFY_THRESHOLD = 6
// 桶中结构转化为红黑树对应table的最小大小
MIN_TREEIFY_CAPACITY = 64
13、hashmap 拓容过程
a、hashmap 默认的容量为16
b、当table 的容量 大于12 会进行一次拓容(resize) 就是当前的 threshold = capacity * loadFactor
c、当 capacity 小于 MIN_TREEIFY_CAOACITY 需要拓容,使用链表结构存储
d、当桶中bin的数量大于了,TREEIFY_THRESHOLD 且 capacity 大于了 MIN_TREEIFY_CAPACITY , 因此,会树化
e、如果桶中bin的数量没有超过TREEIFY_THRESHOLD, 采用链表存储,如果超过 TREEIFY_THRESHOLD,当执行了remve 的时候 桶中bin的数量小于 UNTREEIFY_THRESHOLD(6) 会自动还原为 链表
网友评论