今天复习一下 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,含注释)
在IT领域,尤其是在Web开发中,`Session`和`HashTable`是两个重要的概念,它们在构建功能丰富的应用程序,特别是像购物车这样...如果你在阅读和理解源代码过程中遇到任何问题,可以向提供QQ的作者提问,进行学习交流。
Java集合专题总结:HashMap和HashTable源码学习和面试总结 本文总结了Java集合专题中的HashMap和HashTable,涵盖了它们的源码学习和面试总结。HashMap是一种基于哈希表的集合类,它的存储结构是一个数组,每个元素...
本教程将深入探讨如何使用`Enumeration`接口遍历`HashTable`,并提供详细的源代码实例及指导。`Enumeration`在Java早期版本中用于迭代容器中的元素,虽然在Java集合框架的后续版本中被迭代器(Iterator)所取代,但...
本文将深入探讨基于C语言实现的HashTable,解析其源码,揭示其工作原理和优化策略。 一、哈希表的基本概念 1. 哈希函数:哈希表的核心是哈希函数,它将键转化为数组索引,使得键值可以快速定位。一个好的哈希函数...
在Java编程语言中,`HashMap`和`HashTable`都是实现键值对存储的数据结构,但它们之间存在一些显著的区别,这些区别主要体现在...对于学习和理解,源码阅读是非常有价值的,可以帮助深入理解Java集合框架的设计原理。
通过阅读和理解这段代码,你可以深入学习哈希表的工作原理,并且了解到如何在C语言中实现这一重要的数据结构。同时,这也可以作为进一步优化哈希表性能的基础,比如调整哈希函数、优化冲突解决策略等。
.NET中Hashtable的源代码,Reflector出来的,并且消除了一些错误.可以直接使用.在03下编译通过
这个压缩包“Java 实例 - 遍历 HashTable 的键值源代码+详细教程.zip”包含了关于如何遍历`HashTable`的详细教程和源代码,对于学习Java的初学者或者需要深入了解`HashTable`操作的开发者来说,这是一个非常宝贵的...
了解这些基本概念后,你可以通过阅读源代码来深入理解哈希表的实现细节,例如散列函数的设计、冲突解决策略的实现以及动态调整大小的过程。这对于学习数据结构和算法,特别是对C语言编程者来说,是非常有价值的实践...
阅读STL源代码可以帮助我们理解C++容器和算法的底层实现,从而优化自己的代码,提高程序性能。同时,也能让我们更好地掌握C++模板机制,以及如何利用泛型编程来编写高效且可复用的代码。通过分析源码,我们可以学习...
通过阅读源代码,你可以了解各种算法如何与容器和迭代器协同工作,以及如何实现通用性和效率。 5. **stl_rope.h, ropeimpl.h**:Rope是一种特殊的数据结构,用于高效处理大字符串的拼接和切分操作。它通过将字符串...
通过阅读这些源代码,开发者可以深入理解STL内部的实现细节,比如迭代器的工作方式、容器的内存管理策略以及算法的效率优化。这对于提升C++编程技巧,特别是进行高性能、低级别的内存管理和优化时,是极其宝贵的资源...
标题"hash_src.zip_c hashtable_hash_hashtable"表明这是一个关于C语言实现哈希表的源代码,可能包含了哈希函数和冲突解决策略的实现。"hashtable"通常指的是哈希表的存储结构,而"hash"可能指的是哈希函数。 描述...
【Java容器之Hashtable源码分析】 在Java编程中,`Hashtable`是一个古老的容器类,它继承自`Dictionary`接口,并实现了`Map`接口,同时也实现了`Cloneable`和`Serializable`接口。`Hashtable`与`HashMap`类似,都是...
以下是对"字典源代码--JAVA关于小字典的几个源代码"的详细解释: 1. **Map接口**: Map是Java集合框架的一部分,它定义了存储键值对的方法。主要方法包括`put(key, value)`用于添加元素,`get(key)`用于根据键获取值...
通过查看源代码,开发者可以深入了解其内部实现,包括哈希函数的选择、冲突解决策略(如开放寻址法或链地址法)、内存管理以及对泛型的支持方式等。 在实际开发中,哈希表常用于数据库索引、缓存系统、映射关系的...
哈希表(Hash Table),又称为散列表,是一种数据结构,它通过把关键码值映射到表中一个位置来实现查找,使得插入和查找数据的速度都非常快。在C++编程语言中,虽然标准库没有直接提供哈希表的数据结构,但我们可以...