HashMap
特性
- HashMap存储键值对实现快速存取,允许key、value为null。key值不可重复,若key值重复则覆盖。
- 非同步,线程不安全。
- 底层是hash表,不保证有序(比如插入的顺序)
HashMap的底层原理
基于hashing的原理,jdk8后采用数组+链表+红黑树的数据结构。我们通过put和get存储和获取对象。当我们给put()方法传递键和值时,先对键做一个hashCode()的计算来得到它在bucket数组中的位置来存储Entry对象。当获取对象时,通过get获取到bucket的位置,再通过键对象的equals()方法找到正确的键值对,然后在返回值对象。
存储结构
- 从源码可知,HashMap类中有一个非常重要的字段,就是 Node[] table,即哈希桶数组,明显它是一个Node的数组。源码如下图Node[JDK1.8]
Node是HashMap的一个内部类,实现了Map.Entry接口,本质是就是一个映射(键值对)
2. HashMap就是使用哈希表来存储的。哈希表为解决冲突,可以采用开放地址法和链地址法等来解决问题,Java中HashMap采用了链地址法。链地址法,简单来说,就是数组加链表的结合。在每个数组元素上都一个链表结构,当数据被Hash后,得到数组下标,把数据放在对应下标元素的链表上
hashMap中put实现
- 计算关于key的hashcode值(与Key.hashCode的高16位做异或运算)
- 如果散列表为空时,调用resize()初始化散列表
- 如果没有发生碰撞,直接添加元素到散列表中去
- 如果发生了碰撞(hashCode值相同),进行三种判断
4.1) 若key地址相同或者equals后内容相同,则替换旧值
4.2) 如果是红黑树结构,就调用树的插入方法
4.3) 链表结构,循环遍历直到链表中某个节点为空,尾插法进行插入,插入之后判断链表个数是否到达变成红黑树的阙值8;也可以遍历到有节点与插入元素的哈希值和内容相同,进行覆盖。(jdk8开始采用尾插入) - 如果桶满了大于阀值(0.75),则resize进行扩容
网上找了个便于理解的图
hashMap中get实现
对key的hashCode进行hashing,与运算计算下标获取bucket位置,如果在桶的首位上就可以找到就直接返回,否则在树中找或者链表中遍历找,如果有hash冲突,则利用equals方法去遍历链表查找节点。