实现了Map接口的Hash表。这个实现提供了所有的Map操作,并且允许null值和null键。HashMap可以粗略的视为HashTable,区别是HashMap是非同步的,并且允许null。这个类不保证固定的排序,特别的,不保证顺序一直保持固定不变。例如一个元素在某一时刻的顺序是第3个,未来某一时刻再看他的顺序,可能是第1个。
这个实现可以为get/put基本操作提供常量时间性能,假设哈希函数把元素在buckets中合理的分散。集合视图的迭代时间与HashMap实例的容量(buckets数量)加上它的大小(key-value个数)成正比,因此,如果侧重迭代的性能,就不要设置太高的初始化容量(或者太低的负载因子)。
一个HashMap实例有2个影响性能的参数:初始化容量和负载因子。容量是哈希表中buckets的数据量,初始化容量是哈希表创建时候的容量。负载因子是个权重,表示哈希表在自动增长之前可以装多满。当哈希表的输入数量超过负载因子*当前容量,这个哈希表被rehashed,即内部数据结构重新建造,使这个哈希表有近2倍的buckets数量。
通常,默认的0.75负载因子提供一个时间和空间消耗之间很好的权衡。更大的值可降低空间开销,但是增加查找消耗,被反映在HashMap大多数的操作里,包括get/put。当设置初始化容量时要考虑期望的入口数量和负载因子,将再哈希操作次数最小化。如果初始容量比最大入口数量/负载因子大,没有再哈希操作会发生。
如果一个HashMap实例存储很多映射,创建一个足够大的容量可以使映射的存储效率更高,不会让他自动进行再哈希扩大这个表。注意,多个健对应同一个hashCode()一定会降低哈希表的性能。为了改善影响,当键可比较时,这个类可能会利用键的比较顺序来帮助打破多对一的关系。
注意,这个实现是不同步的,如果多个线程同时访问一个HashMap,并且至少一个线程结构性的修改了映射,它必须在HaspMap外面被同步。结构性修改是任意一个增加/删除1个或多个映射的操作,仅仅是修改一个实例中已有键关联的值不是一个结构性修改。这个一般是在封装这个映射的某些对像上同步来完成的。
如果没有这种对象存在,这个映射应该使用Collection.syncchronizedMap方法包装起来。这个最好在创建的时候做好,为了避免偶然的对映射的非同步访问:
Map m = Collections.synchronizedMap(new HashMap(...));
从这个类的所有集合视图方法返回的迭代器都是fail-fast:如果这个映射在迭代器创建后的任一时刻被结构性的改变,使用除了这个迭代器的remove方法之外的任意一种方式,这个迭代器都会抛出ConcurrentModificationException。因此面对并发修改,迭代器干净利落的失败,而不是在未来某一个不确定的时间出现任意不确定的冒险行为。
注意,迭代器的fail-fast行为不能被保证,通常来讲,不可能在非同步的并发修改的表现上做任何硬性保证。Fail-fast的迭代器会尽力抛出ConcurrentModificationException。因此,要依赖这个异常写程序来保证正确性是不对的。迭代器的Fail-fast行为应该只用来探测Bug。
HashMap存储的数据效率很高, - [时间复杂度 ] 。使用的数据是数组,链表和红黑树。默认容量是16,使用时建议指定初始化容量大小,计算初始化容量大小的方法是:a=预期的容量/0.75,取大于a的最小(2的k次幂)。
先是以数组的存储数据,当产生了哈希冲突,在数组同一个位置以单链表存储数据,当链表长度大于8时,将单链表转化为红黑树存储数据。
红黑树是自平衡的二叉搜索树,依靠左旋,右旋和变色实现自平衡。红黑输依靠一下性质进行自平衡:
每个节点都有红色或黑色
树的根始终是黑色的 (黑土地孕育黑树根, )
没有两个相邻的红色节点(红色节点不能有红色父节点或红色子节点,并没有说不能出现连续的黑色节点)
从节点(包括根)到其任何后代NULL节点(叶子结点下方挂的两个空节点,并且认为他们是黑色的)的每条路径都具有相同数量的黑色节点