1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-20 22:17:09 +08:00

tableSizeFor()方法

tableSizeFor()方法保证了 HashMap 总是使用2的幂作为哈希表的大小
This commit is contained in:
SnailClimb 2018-09-29 15:52:28 +08:00 committed by GitHub
parent f94310ade0
commit ec28415ae4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -123,9 +123,46 @@ static int hash(int h) {
1. **线程是否安全:** HashMap 是非线程安全的HashTable 是线程安全的HashTable 内部的方法基本都经过 `synchronized` 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);
2. **效率:** 因为线程安全的问题HashMap 要比 HashTable 效率高一点。另外HashTable 基本被淘汰,不要在代码中使用它;
3. **对Null key 和Null value的支持** HashMap 中null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。。但是在 HashTable 中 put 进的键值只要有一个 null直接抛出 NullPointerException。
4. **初始容量大小和每次扩充容量大小的不同 ** ①创建时如果不指定容量初始值Hashtable 默认的初始大小为11之后每次扩充容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充容量变为原来的2倍。②创建时如果给定了容量初始值那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。
4. **初始容量大小和每次扩充容量大小的不同 ** ①创建时如果不指定容量初始值Hashtable 默认的初始大小为11之后每次扩充容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充容量变为原来的2倍。②创建时如果给定了容量初始值那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小HashMap 中的`tableSizeFor()`方法保证,下面给出了源代码)。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。
5. **底层数据结构:** JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化当链表长度大于阈值默认为8将链表转化为红黑树以减少搜索时间。Hashtable 没有这样的机制。
**HasMap 中带有初始容量的构造函数:**
```java
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
```
下面这个方法保证了 HashMap 总是使用2的幂作为哈希表的大小。
```java
/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
```
## HashMap 的长度为什么是2的幂次方
为了能让 HashMap 存取高效尽量较少碰撞也就是要尽量把数据分配均匀。我们上面也讲到了过了Hash 值的范围值-2147483648到2147483648前后加起来大概40亿的映射空间只要哈希函数映射得比较均匀松散一般应用是很难出现碰撞的。但问题是一个40亿长度的数组内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ `(n - 1) & hash` ”。n代表数组长度。这也就解释了 HashMap 的长度为什么是2的幂次方。