HashMap是一个散列表,是基于拉链法实现的。这个类继承了Map接口,Map接口提供了所有的哈希操作,比如set()、put()、remove()等,并且允许操作的键值对为null。HashMap跟Hashtable基本相同,区别是HashMap是非同步的并且允许键值对为null。HashMap不保证映射的顺序,特别是不保证该顺序恒久不变。
在用到的哈希函数均匀性比较好的前提下,基本操作比如put和get的时间都是O(1)的。处理哈希冲突所需的时间与哈希表的容量(桶的数目)加上表的大小(存储的键值映射对)之和成正比。所以在初始化哈希表时,初始容量不要太大(装载因子不要太小)。
影响HashMap性能的两个比较重要的参数是初始容量(initial capacity)和装载因子(load factor)。容量(capacity)是哈希表中桶的数量,初始容量就是哈希表创建时桶的数量。装载因子(load factor)是一个参数,它用来衡量哈希表满和空的程度,它在公式上等于size(键值对数量)/capacity(容量)。当装载因子大于某个值时,哈希表会进行重构(rehash),一般会将桶的数目加倍,哈希表的初始容量一般设为2的幂。
负载因子一般设为0.75。这是时间费用和空间费用的比较好的权衡。负载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。
HashMap是非同步的,如果有多个线程并发的的访问,并且只是有个线程在结构上改变哈希表,必须增加额外的同步策略。这里所说的在结构上该表哈希表是指增加或删除一个或多个键值对,只是改变某个关键字所对应的值并不是指改变哈希表结构。
HashMap的数据结构
在Java编程语言中,最基本的结构就是两种,一个是数组,一个是模拟指针(引用),所有的数据结构都可以用这两个结构来构造,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。这是因为HashMap使用拉链法来解决哈希冲突的。
从构造函数可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
1 static class Entry<K,V> implements Map.Entry<K,V> { 2 final K key; 3 V value; 4 Entry<K,V> next; 5 final int hash; 6 7 ........ 8 }