@@ -72,15 +72,13 @@ BlockingQueue 接口的实现类有 ArrayBlockingQueue、DelayQueue、LinkedBloc
72
72
73
73
阻塞指的是一种程序执行状态,其中某个线程在等待某个条件满足时暂停其执行(即阻塞),直到条件满足时恢复其执行。
74
74
75
- 推荐阅读:[ 阻塞队列BlockingQueue] ( https://javabetter.cn/thread/BlockingQueue.html ) 。
76
-
75
+ 推荐阅读:[ 阻塞队列 BlockingQueue] ( https://javabetter.cn/thread/BlockingQueue.html ) 。
77
76
78
77
> 1 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的用友金融一面原题:你了解哪些集合框架?
79
78
> 2 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的华为一面原题:说下 Java 容器和 HashMap
80
79
> 3 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的小米暑期实习同学 E 一面面试原题:你了解哪些集合?
81
80
> 4 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的美团面经同学 16 暑期实习一面面试原题:知道哪些集合,讲讲 HashMap 和 TreeMap 的区别,讲讲两者应用场景的区别;讲一下有哪些队列,阻塞队列的阻塞是什么含义?
82
81
83
-
84
82
## List
85
83
86
84
### 2.ArrayList 和 LinkedList 有什么区别?
@@ -582,11 +580,15 @@ $2^n$ 的二进制形式为 1,后面跟着 n 个 0,那 $2^n$ - 1 的二进
582
580
> 1 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的小米春招同学 K 一面面试原题:为什么是 2 次幂 到什么时候开始扩容 扩容机制流程
583
581
> 2 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的支付宝面经同学 2 春招技术一面面试原题:hashCode 对数组长度取模定位数组下标,这块有没有优化策略?
584
582
585
- ### 16.如果初始化 HashMap,传一个 17 的值` new HashMap<> ` ,它会怎么处理?
583
+ ### 16.如果初始化 HashMap,传一个 17 容量,它会怎么处理?
584
+
585
+ HashMap 会将这个值转换为大于或等于 17 的最小的 2 的幂。这是因为 HashMap 的设计是基于哈希表的,而哈希表的大小最好是 2 的幂,这样可以优化哈希值的计算,并减少哈希冲突。
586
+
587
+ 所以,如果你传入 17 作为初始容量,HashMap 实际上会被初始化为大小为 32 的哈希表。
586
588
587
- 简单来说,就是初始化时,传的不是 2 的倍数时,HashMap 会向上寻找 ` 离得最近的2的倍数 ` ,所以传入 17,但 HashMap 的实际容量是 32。
589
+ ![ 三分恶面渣逆袭:容量计算 ] ( https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/sidebar/sanfene/collection-18.png )
588
590
589
- 我们来看看详情, 在 HashMap 的初始化中 ,有这样⼀段⽅法;
591
+ 在 HashMap 的初始化构造方法中 ,有这样⼀段代码:
590
592
591
593
``` java
592
594
public HashMap(int initialCapacity, float loadFactor) {
@@ -596,26 +598,45 @@ public HashMap(int initialCapacity, float loadFactor) {
596
598
}
597
599
```
598
600
599
- - 阀值 threshold ,通过⽅法` tableSizeFor ` 进⾏计算,是根据初始化传的参数来计算的。
600
- - 同时,这个⽅法也要要寻找⽐初始值⼤的,最⼩的那个 2 进制数值。⽐如传了 17,我应该找到的是 32。
601
+ 阀值 threshold 会通过⽅法` tableSizeFor() ` 进⾏计算。
601
602
602
603
``` java
603
604
static final int tableSizeFor(int cap) {
604
- int n = cap - 1 ;
605
- n |= n >>> 1 ;
606
- n |= n >>> 2 ;
607
- n |= n >>> 4 ;
608
- n |= n >>> 8 ;
609
- n |= n >>> 16 ;
610
- return (n < 0 ) ? 1 : (n >= MAXIMUM_CAPACITY ) ? MAXIMUM_CAPACITY : n + 1 ; }
605
+ int n = cap - 1 ;
606
+ n |= n >>> 1 ;
607
+ n |= n >>> 2 ;
608
+ n |= n >>> 4 ;
609
+ n |= n >>> 8 ;
610
+ n |= n >>> 16 ;
611
+ return (n < 0 ) ? 1 : (n >= MAXIMUM_CAPACITY ) ? MAXIMUM_CAPACITY : n + 1 ;
612
+ }
611
613
```
612
614
613
- - MAXIMUM_CAPACITY = 1 << 30,这个是临界范围,也就是最⼤的 Map 集合。
614
- - 计算过程是向右移位 1、2、4、8、16,和原来的数做` | ` 运算,这主要是为了把⼆进制的各个位置都填上 1,当⼆进制的各个位置都是 1 以后,就是⼀个标准的 2 的倍数减 1 了,最后把结果加 1 再返回即可。
615
+ ①、` int n = cap - 1; ` 将传入的容量减 1,用于确保如果传入的容量已经是 2 的幂次方,计算结果不会超过这个数。
616
+
617
+ ②、接下来通过不断右移(` >>> ` )并与自身进行或运算(` |= ` ),将 n 的二进制表示中的所有低位设置为 1。
618
+
619
+ - ` n |= n >>> 1; ` 把 n 的二进制表示中最高位的 1 之后的一个 0 变成 1。
620
+ - ` n |= n >>> 2; ` 接着把后两位中的 0 都变成 1。
621
+ - 依此类推,直到 ` n |= n >>> 16; ` ,此时 n 的二进制表示中,从最高位的 1 开始到最低位,都变成了 1。
622
+
623
+ ③、如果 n 小于 0,说明 cap 是负数,直接返回 1(理论上哈希表的大小不应该是负数或 0)。
615
624
616
- 以 17 为例,看一下初始化计算 table 容量的过程:
625
+ 如果 n 大于或等于 MAXIMUM_CAPACITY(通常是$2^{30}$),则返回 MAXIMUM_CAPACITY。
617
626
618
- ![ 容量计算] ( https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/sidebar/sanfene/collection-18.png )
627
+ 否则,返回 n + 1,这是因为 n 的所有低位都是 1,所以 n + 1 就是大于 cap 的最小的 2 的幂次方。
628
+
629
+ #### 初始化 HashMap 的时候需要传入容量值吗?
630
+
631
+ 在创建 HashMap 时可以指定初始容量值。这个容量是指 Map 内部用于存储数据的数组大小。
632
+
633
+ 如果预先知道 Map 将存储大量键值对,提前指定一个足够大的初始容量可以减少因扩容导致的重哈希(rehashing)操作,从而提高性能。
634
+
635
+ 因为每次扩容时,HashMap 需要新分配一个更大的数组并重新将现有的元素插入到这个新数组中,这个过程相对耗时,尤其是当 Map 中已有大量数据时。
636
+
637
+ 当然了,过大的初始容量会浪费内存,特别是当实际存储的元素远少于初始容量时。如果不指定初始容量,HashMap 将使用默认的初始容量 16。
638
+
639
+ > 1 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的奇安信面经同学 1 Java 技术一面面试原题:map 集合在使用时候一般都需要写容量值?为什么要写?扩容机制?
619
640
620
641
### 17.你还知道哪些哈希函数的构造方法呢?
621
642
@@ -844,7 +865,7 @@ final int hash(Object k) {
844
865
845
866
当然了,这个功劳既属于新的哈希算法,也离不开 n 为 2 的整数次幂这个前提,这是它俩通力合作后的结果 ` hash & (newCapacity - 1) ` 。
846
867
847
- #### 那你说说扩容的时候每个节点都要进行位运算吗,如果我这个HashMap里面有几十万条数据 ,都要进行位运算吗?
868
+ #### 那你说说扩容的时候每个节点都要进行位运算吗,如果我这个 HashMap 里面有几十万条数据 ,都要进行位运算吗?
848
869
849
870
在 JDK 8 的新 hash 算法下,数组扩容后的索引位置,要么就是原来的索引位置,要么就是“原索引+原来的容量”,遵循一定的规律。
850
871
@@ -853,7 +874,8 @@ final int hash(Object k) {
853
874
所以,尽管有几十万条数据,每个数据项的位置决定仅需要一次简单的位运算。位运算的计算速度非常快,因此,尽管扩容操作涉及遍历整个哈希表并对每个节点进行操作,但这部分操作的计算成本是相对较低的。
854
875
855
876
> 1 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的小米春招同学 K 一面面试原题:为什么是 2 次幂 到什么时候开始扩容 扩容机制流程
856
- > 2 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的小米暑期实习同学 E 一面面试原题:说说HashMap的扩容机制,1.8扩容具体实现
877
+ > 2 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的小米暑期实习同学 E 一面面试原题:说说 HashMap 的扩容机制,1.8 扩容具体实现
878
+ > 3 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的奇安信面经同学 1 Java 技术一面面试原题:map 集合在使用时候一般都需要写容量值?为什么要写?扩容机制?
857
879
858
880
### 22.jdk1.8 对 HashMap 主要做了哪些优化呢?为什么?
859
881
@@ -920,7 +942,7 @@ HashMap 不是线程安全的,主要有以下几个问题:
920
942
因为线程 1 执行完 table = newTab 之后,线程 2 中的 table 此时也发生了变化,此时去 get 的时候当然会 get 到 null 了,因为元素还没有转移。
921
943
922
944
> 1 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的华为 OD 原题:HashMap 是线程安全的吗?
923
- > 2 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的华为面经同学 8 技术二面面试原题:HashMap是线程安全的吗 ?
945
+ > 2 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的华为面经同学 8 技术二面面试原题:HashMap 是线程安全的吗 ?
924
946
> 3 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的字节跳动面经同学 9 飞书后端技术一面面试原题:HashMap 为什么不安全,如何改进,以及 ConcurrentHashMap
925
947
926
948
### 25.有什么办法能解决 HashMap 线程不安全的问题呢?
@@ -944,10 +966,9 @@ Hashtable 也是线程安全的,但它的使用已经不再推荐使用,因
944
966
![ 初念初恋:ConcurrentHashMap 8 中的实现] ( https://cdn.tobebetterjavaer.com/stutymore/map-20230816155924.png )
945
967
946
968
> 1 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的小米春招同学 K 一面面试原题:有哪些线程安全的 map,ConcurrentHashMap 怎么保证线程安全的,为什么比 hashTable 效率好
947
- > 2 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的华为面经同学 8 技术二面面试原题:Java中的线程安全的集合是什么 ?
969
+ > 2 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的华为面经同学 8 技术二面面试原题:Java 中的线程安全的集合是什么 ?
948
970
> 3 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的字节跳动面经同学 9 飞书后端技术一面面试原题:HashMap 为什么不安全,如何改进,以及 ConcurrentHashMap
949
971
950
-
951
972
### 27.HashMap 内部节点是有序的吗?
952
973
953
974
HashMap 是无序的,根据 hash 值随机插入。如果想使用有序的 Map,可以使用 LinkedHashMap 或者 TreeMap。
0 commit comments