今天复习一下 Hashtable 的基本原理。看了下源码,找了点资料。
下面这个文章,写得相当完整了。
http://tonylian.iteye.com/blog/384080
以下简称引文。
这篇文章是以 .net 为例的。
java 的有一点区别。
一、初始容量 和加载因子
Hashtable 的实例有两个参数影响其性能:初始容量和加载因子。容量 是哈希表中桶 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子 是对哈希表在其容量自动增加之前可以达到多满的一个尺度。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。
通常,默认加载因子(.75)在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数 Hashtable 操作中,包括 get 和 put 操作,都反映了这一点)。
默认初始化,初始容量为 11,加载因子为 0.75。
public Hashtable() {
this(11, 0.75f);
}
引文中有如下描述:
数组容量 Array.Length 期望是一个 "比较大的质数", 这样 f(K) = HashOf(K) % Array.Length 取模运算之后得出的数冲突机会较小. 想象一个极端例子, 假设 Array.Length = 2, 则只要 HashOf(K) 是偶数, f(k) 都为 0. 所以说哈希表的实际容量一般都是有规律的, 和数组不一样, 不能任意设置.
二、hashtable 的存取
引文中描述到:
Hashtable 中的实际数据都存储在一个内部 Array 中 (当然和普通数组一样, 有固定容量, 上下标, 以数字索引存取), 当用户希望取得 Hashtable[K] 值的时候, Hashtable 进行如下处理:
[1] 为了保证 f(K) 的取值范围在 0 <= f(K) < Array.Length, 函数 f 的关键步骤是取模运算, 算得实际数据存储位置为 f(K) = HashOf(K) % Array.Length, 至于这个 HashOf(K) 怎么算出来的, 简单举例来说她可以取关键字的 ASCII 码根据一定规则运算得到.
[2] 如果发生多个 K 值的哈希值重复, 即 f(K1) = f(K2), 而 f(K1) 位置已经有数据占用了, Hashtable 采用的是 "开放定址法" 处理冲突, 具体行为是把 HashOf(K2) % Array.Length 改为 (HashOf(K2) + d(K2)) % Array.Length , 得出另外一个位置来存储关键字 K2 所对应的数据, d 是一个增量函数. 如果仍然冲突, 则再次进行增量, 依此循环直到找到一个 Array 中的空位为止. 将来查找 K2 的时候先搜索 HashOf(K2) 一档, 发现不是 K2, 那么增量 d(K2) 继续搜索, 直到找到为止. 连续冲突次数越多, 搜索次数也越多, 效率越低.
put 和 get 的源码
public synchronized V get(Object key) {
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K, V> e = tab[index]; e != null; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K, V> e = tab[index]; e != null; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}
modCount++;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
Entry<K, V> e = tab[index];
tab[index] = new Entry<K, V>(hash, key, value, e);
count++;
return null;
}
其中,这一句比较关键。
int index = (hash & 0x7FFFFFFF) % tab.length;
tab.length 就是容量。
更详细的,可以自行阅读一下源码。
分享到:
相关推荐
HashTable源码(JDK1.7,含注释)
HashTable源码
Java 实例 - 使用 Enumeration 遍历 HashTable源代码+详细指导教程.zip
Java集合专题总结:HashMap 和 HashTable 源码学习和面试总结
Session HashTable 实现的购物车源代码 大家看看 如果有什么不知道的可以问我 QQ529165947 大家互相学习交流
C/C++语言 hashtable代码 .c文件 适用于linux ubuntu unix等平台 terminal中操作
.NET中Hashtable的源代码,Reflector出来的,并且消除了一些错误.可以直接使用.在03下编译通过
希哈表C源代码工程hashtable,实例工程,下载可以直接运行使用。
hashtable hash表 散列表 C++ 源代码。还是非常不错的资源。
Java 实例 - 遍历 HashTable 的键值源代码+详细教程.zip
Hashtable源码分析目录一、`Hashtable`容器概述(==一图以蔽之==)二、`Hashtable`类的属性三、`Hashtable`类的构造器四、增加`key-value`相关方法1、`addEntry`方法2、`put`方法3、`putAll`方法五、删除`key-value`...
基于原子操作的无锁hashtable源码
NULL 博文链接:https://qiaolevip.iteye.com/blog/2094447
不过现在写代码那个SecurityAttribute用得是甚少――这个方面的东西除了照例子依样画葫芦过一下在实践中根本是没有涉足。 要序列化Hashtable其实就只是少一个整数类型的Item属性而已,在这上面是没有什么办法了。...
本篇文章是对PHP中的HashTable结构进行了详细的分析介绍,需要的朋友参考下
this is a hash table using hash function
理解了HashTable的数据存储结构,对我们分析PHP的源代码,特别是Zend Engine中的虚拟机的实现时,有很重要的帮助。它可以帮助我们在大脑中模拟一个完整的虚拟机的形象。它也是PHP中其它一些数据结构如数组实现的基础...
import java.awt.*;... private Hashtable userPwds = new Hashtable(); private Vector usersOnline = new Vector(); private Vector usersDisOnline = new Vector(); private long clickTime=0;