Map是广义java集合框架中的另外一部分,HaspMap作为框架中使用频率最高类型之一,我们有必要去深入了解它。
对比 HashMap,HashTable,TreeMap
相同点
- HashMap,HashTable,TreeMap都是一些常见的map实现,是以键值对的形式存储和操作数据的容器类型。
不同点
- HashTable是早期的Java类库提供的的一个哈希表实现,本身是同步的,不支持null键和空值,由于同步导致的性能开销,使用相当较少。
- HashMap是应用更加广泛的哈希表实现,行为上大致和haspTable大致相同,主要区别在于HashMap不是同步的,支持null键和空值。所以性能上更加优秀。
- TreeMap是基于红黑树的一种提供顺序访问的Map,和HashMap不同,他的get,put,remove之类的操作都是O(log(n))的时间复杂度,具体顺序可以由指定的Comparator来决定,或者根据键的自然顺序来判断。
HashMap常见问题
haspMap明确声明不是线程安全的的数据结构,所以haspMap在并发环境可能出现无限循环占用cpu,size不准确的诡异问题。
知识扩展
map结构
hashMap等其他map实现都hi扩展abstractMap,里面包含了通用的抽象方法,HashMap的性能表现非常依赖于哈希码的有效性。
- equals相等,hashcode一定要相等。
- 重写了hashCode也要重写equals。
- hashCode需要保持一致性,状态改变的哈希值仍然要一致。
- equals的对称,反射,传递。
- LinkedHashMap 和 TreeMap 都可以保证某种顺序,但二者还是很不同的
- linkedHashMap通常提供的是遍历顺序符合插入顺序,他的实现是通过条目维护一个双向链表。
对于TreeMap他的整体顺序是由键的顺序关系决定的,通过compartoar来决定。
HashMap源码分析HashMap 内部实现基本点分析。
- 容量(capacity)和负载系数(load factor)。
- 树化
HashMap的内部结构,可以看作是数组和链表组成的复合结构,数组被分成一个个桶数组,通过哈希值在这个桶数组寻址,哈希值相同的键值对,则以链表形式存储。
从非拷贝构造函数的实现来看,这个表格(数组)似乎并没有在最初就初始化好,仅仅设置了一些初始值而已。
1 |
|
然后看put方法实现
1 |
|
put里面调用了putVal方法
1 |
|
从源码种可以看出
如果表格是null,resize会初始化它
resize方法兼顾两个职责,创建初始存储表格,或者在容量不满足需求的时候进行扩容
在放置新的键值对的时候,如果发生以下情况,就会发生扩容
1 |
|
具体键值在哈希表种的位置取决于下面的运算
1 |
|
最后hashMap为什么要树化,本质上是安全问题。因为在元素放置过程中,如果一个对象的哈希冲突,都被放置在一个桶中,会形成一个链表,链表查询是线性的会严重影响存取的性能。