Map 接口
哈希表就是一种以键-值(key-indexed) 存储数据的结构,只要输入待查找的值即 key,即
可查找到其对应的值。哈希的思路很简单,如果所有的键 hashCode 都是整数,那么就可以使用一个简单数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。简单的计算方法 hashcode%数组长度=【0,数组的长度-1】它提供了一组键值的映射。其中存储的每个数据对象都有一个相应的键 key,键决定了值对象在 Map 中的存储位置。键应该是唯一的,不允许重复,每个 key 只能映射一个 value。
map的定义
定义 map 对象时需要指定 key 和 value 对应的类型,必须是复杂类型,不能使用 int。map 接口中有一个内部接口为 Entry:interface Entry<K,V>封装所存储的 key-value 对数据
interface Entry<K,V> {
K getKey(); 因为 key 决定数据的存放位置,一旦存储成功则 key 不允许修改
V getValue(); 针对 value 提供 get/set 方法
V setValue(V value);
//存储的数据应该是可以进行等值判断的,key 不允许重复
boolean equals(Object o);
int hashCode();
}
结论:每个 Entry 对象中封装了一个 key 和 value
另外 Entry 接口中通过静态方法提供了一组比较器的默认实现
用于规范其中存放数据的规则,map 中所存放的数据将给封装为一个一个的 Entry,一个key/value 对对应一个 Entry 对象
map常使用的方法
Object put(Object key,Object value):用来存放一个键-值对 Map 中 ,如果出现 key 值冲突则后盖前。允许 key 值和 value 值为 null,但是 key 值为 null 只能有一个,value 值为null 没有个数限制
map的三种示图
map的具体实现类
HashMap
静态内部类用于实现 Entry,HahMap 中存放的 key/value 对就被封装为 Node 对象。其中 key 就是存放的键值,用于决定具体的存放位置;value 是具体存放的数据,hash 就是当前 Node 对象的 hash 值,next 用于指向下一个 Node 节点(单向链表)具体存储数据的实现采用的是单向链
重要的阈值
static final int TREEIFY_THRESHOLD = 8;//树化阈值:即链表转成红黑树的阈值,在存储数据时,当链表长度 > 该值时,则将链表转换成红黑树
static final int UNTREEIFY_THRESHOLD = 6;//桶的链表还原阈值:即红黑树转为链表的阈值,当在扩容(resize())时(此时 HashMap 的数据存储位置会重新计算),在重新计算存储位置后,当原有的红黑树内数量 < 6 时,则将 红黑树转换成链表
构造器
获取的值为获取 2n<初始化容积<2(n+1)时返回 2(n+1)
,返回一个大于等于初始化容积值的 2n值tableSizeFor 用于计算容积值,不是设置多少就是多少。就是计算一个 2**n 值>=设定的容积值.例如初始化容积参数值为 7 则实际创建容积值为 8注意:在构造器中并没有创建任何用于存储数据的集合—延迟加载,第一次存储数据时才进行空间分配、
Hashtable
Hashtable:线程安全的,不允许 null 的键或值;是线程安全的,但是 Hashtable 线程安全的策略实现代价却太大了,简单粗暴,get/put 所有相关操作都是 synchronized 的,这相当于给整个哈希表加了一把大锁。多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞,相当于将所有的操作串行化,在竞争激烈的并发场景中性能就会非常差
- Hashtable 不允许 null 值或者 null 键,编译时不会报错,但是运行报错。
- HashMap 允许 null 值或者 null 键,只是 key 为 null 只能一次,value 为 null 没有限制
- HashMap 的实现上没有同步约束,但是 Hashtable 的实现方法上有 synchronized 同步约束,所以说 Hashtabe 属于一个线程安全的类