74 lines
2.3 KiB
Markdown
74 lines
2.3 KiB
Markdown
---
|
||
title: java散列知识点总结
|
||
date: 2017-02-18 19:19:01
|
||
category: notes
|
||
tags:
|
||
- Algorithms
|
||
keywords:
|
||
- hash
|
||
- 散列
|
||
- 哈希
|
||
- java
|
||
---
|
||
|
||
java 的根类 `Object` 具有 `hashcode` 方法。当 `equal` 方法被重写时也应当重写 `hashcode` 方法。
|
||
|
||
## 基本数据类型的散列码
|
||
|
||
* `byte` `short` `int` `char` 类型的搜索键将会转换为 `int`。
|
||
* `float` 类型的搜索键使用 `Float.floatToIntBits(key)` 作为散列码。
|
||
* `long` 类型的搜索键会进行折叠操作,如下:
|
||
|
||
```java
|
||
iny hashCode = (int) (key ^ (key >> 32));
|
||
```
|
||
|
||
* `double` 类型的搜索键会使用 `Double.doubleToLongBits(key)` 方法转换为 `long` 类型然后再进行折叠。
|
||
|
||
## 字符串类型的散列码
|
||
|
||
对于字符串一般使用多项式散列码进行计算,
|
||
|
||
~~这里放个公式的图~~
|
||
|
||
b的较好取值为31,33,37,39,41。在 java String 类中 `b` 取31。
|
||
|
||
```java
|
||
public static int hash(String key, int tableSize)
|
||
{
|
||
int hashVal = 0;
|
||
|
||
for (int i = 0; i < key.length(); i++)
|
||
hashVal = 37*hashVal + key.charAt(i);
|
||
|
||
hashVal %= tableSize;
|
||
if (hashVal < 0)
|
||
hashVal += tableSize;
|
||
|
||
return hashVal;
|
||
}
|
||
```
|
||
|
||
## 压缩散列码
|
||
|
||
由于散列码可能是很大的正数,通常应该对其进行压缩以防止超出索引的范围。若索引范围为 `0 ~ n - 1` ,通常的做法是 `h(hashCode) = hashCode % N` ,选择N为大于2的素数。
|
||
`java.util.HashMap` 的实现中,将N设置为2的幂值,这样可以使用位运算代替上述的取模:`h(hashCode) = hashCode & (N - 1)` ,两者是完全等价的。
|
||
|
||
## 处理冲突
|
||
|
||
### 开放地址法
|
||
|
||
开放地址法是在冲突发生时,在散列表中找到一个开放位置的过程。
|
||
|
||
* 线性探测,存在成簇问题
|
||
* 二次探测,存在二次成簇问题,并且不能保证一个开放的单元总是可以被找到。
|
||
* 再哈希法
|
||
|
||
### 链地址法
|
||
|
||
链地址法是将具有同样索引的条目放在同一位置,每个位置使用一个桶(ArrayList or LinkedList)来放置多个条目。
|
||
|
||
## 装填因子
|
||
|
||
装填因子衡量一个散列表有多满。`lamda = n / N` 。对于开放地址法,装填因子介于 0 ~ 1,对于链地址法,装填因子可能为任意值。通常开放地址法需要将装填因子维持在0.5以下,而链地址法为0.9以下。`java.util.HashMap` 采用了阈值0.75。
|