携手创作,共同成长!这是我参与「掘金日新计划 · 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的幂。