HashMap【Java数据结构】

83 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情

总结Java 数据结构的方式

  • 功能
  • 是否线程安全
  • 是否接受null值
  • 结构
  • 查找
  • 扩容

HashMap

存放键值对,基于哈希表

非线程安全

可以存储null的key 和 value ,但是null作为key只能有一个

数组加链表,通过拉链法解决哈希冲突问题,当链表长度大于阈值默认为8,并且数组长度大于64,将链表转化为红黑树,减少搜索时间。

hashmap默认大小为16,每次扩容2倍

底层数据结构详解

hash底层就是数组和链表

插入和查找元素时,通过key的hashcode经过hash扰动函数得到hash值,然后又用(n - 1) & hash判断位置。

扰动函数就是hashmap的hash方法,为了防止比较差的hashcode,减少hash冲突。

对(n - 1) & hash 的理解:

在其他地方比如redis中,哈希表这个数据结构都是使用哈希值%哈希表大小的方式获取元素存放的位置。

这里使用(n - 1) & hash 能有什么提升吗。

其实(n - 1) & hash相当于 % ,下面举个例子。

100 % 8 = 4

100 = 1100100

8 = 2^3 = 1000

8 - 1 = 111

& 运算后得 100 = 4;

到此,可以看出两种运算结果是一致的。而二进制运算一定比取余运算效率高。这里也可以解释为什么,hash表每次扩容都是2倍,为了2次幂的成立。

查找

hashmap 是数组+链表时,o(n)

hashmap 是数组+红黑树时,O(logn)

扩容

决定什么时候扩容的是threshold = capacity * loadFactor

当size >= threshold时,就需要扩容。扩容时,是新建一个节点列表。

loadFactor加载因子

越接近于1,代表元素越多越密,越接近于0,越少越稀。

官方给默认值是0.75f。

与其他数据结构比较

HashTable 与 HashMap

  • 是否线程安全 hashtable 线程安全内部方法通过synchronized修饰
  • 效率 hashmap高,因为线程安全问题的处理
  • 对null的支持 hashtable不支持null的key 和value
  • 初始容量 hashmap 16 ,hashtable11
  • 底层结构 hashtable没有 hashmap的机制

ConcurrentHashMap 和 HashMap比较

  • 结构 1.8ConcurrentHashMap采用和hashmap一样的结构
  • 线程安全:

1.7的ConcurrentHashMap,使用的是segment + hashEntry+链表,每次只锁一部分数据,提高并发量

1.8 ConcurrentHashMap使用node + hashEntry+链表+红黑树。并发控制使用synchronized 和CAS来操作。粒度更细

总结

hashmap是一个存放键值对的数据结构,hashmap是非线程安全的,能够存放一个null值的key和多个null值的值。现在的hashmap使用的结构是数组+链表+红黑树。在插入元素后,判断链表节点数量是否大于8,如果大于8数组长度是否大于64,都大于就转化成红黑树。然后扩容涉及到的内容是,hashmap默认16的容量,Threshold,当size大于等于它时就会扩容。Threshold = capacity * loadFactory。加载因子,越接近1,元素越多越密,越接近0,越少越稀。0.75是官方的建议。另外,(n - 1)& hash是在计算元素存放位置出现的,&运算结果和传统计算%的结果一致,但是二进制运算效率更高,同时也是为什么要保持每次扩容2倍,保证2的幂。