Skip to content

Commit ec28415

Browse files
authored
tableSizeFor()方法
tableSizeFor()方法保证了 HashMap 总是使用2的幂作为哈希表的大小
1 parent f94310a commit ec28415

File tree

1 file changed

+38
-1
lines changed

1 file changed

+38
-1
lines changed

Java相关/这几道Java集合框架面试题几乎必问.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,46 @@ static int hash(int h) {
123123
1. **线程是否安全:** HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 `synchronized` 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);
124124
2. **效率:** 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;
125125
3. **对Null key 和Null value的支持:** HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛出 NullPointerException。
126-
4. **初始容量大小和每次扩充容量大小的不同 :** ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。
126+
4. **初始容量大小和每次扩充容量大小的不同 :** ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小(HashMap 中的`tableSizeFor()`方法保证,下面给出了源代码)。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。
127127
5. **底层数据结构:** JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
128128

129+
**HasMap 中带有初始容量的构造函数:**
130+
131+
```java
132+
public HashMap(int initialCapacity, float loadFactor) {
133+
if (initialCapacity < 0)
134+
throw new IllegalArgumentException("Illegal initial capacity: " +
135+
initialCapacity);
136+
if (initialCapacity > MAXIMUM_CAPACITY)
137+
initialCapacity = MAXIMUM_CAPACITY;
138+
if (loadFactor <= 0 || Float.isNaN(loadFactor))
139+
throw new IllegalArgumentException("Illegal load factor: " +
140+
loadFactor);
141+
this.loadFactor = loadFactor;
142+
this.threshold = tableSizeFor(initialCapacity);
143+
}
144+
public HashMap(int initialCapacity) {
145+
this(initialCapacity, DEFAULT_LOAD_FACTOR);
146+
}
147+
```
148+
149+
下面这个方法保证了 HashMap 总是使用2的幂作为哈希表的大小。
150+
151+
```java
152+
/**
153+
* Returns a power of two size for the given target capacity.
154+
*/
155+
static final int tableSizeFor(int cap) {
156+
int n = cap - 1;
157+
n |= n >>> 1;
158+
n |= n >>> 2;
159+
n |= n >>> 4;
160+
n |= n >>> 8;
161+
n |= n >>> 16;
162+
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
163+
}
164+
```
165+
129166
## HashMap 的长度为什么是2的幂次方
130167

131168
为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648到2147483648,前后加起来大概40亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个40亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ `(n - 1) & hash` ”。(n代表数组长度)。这也就解释了 HashMap 的长度为什么是2的幂次方。

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy