15.10.5 Hashtable
散列表(Hashtable)是原始java.util中的一部分同时也是Dictionary的一个具体实现。然而,Java 2重新设计了散列表(Hashtable)以便它也能实现映射(Map)接口。因此现在Hashtable也被集成到类集框架中。它与HashMap相似,但它是同步的。和HashMap一样,Hashtable将关键字/值对存储到散列表中。使用Hashtable时,指定一个对象作为关键字,同时指定与该关键字相关联的值。接着该关键字被散列,而把得到的散列值作为存储在表中的值的下标。散列表仅仅可以存储重载由Object定义的hashCode( )和equals( )方法的对象。hashCode( )方法计算和返回对象的散列码。当然,equals( )方法比较两个对象。幸运的是,许多Java内置的类已经实现了hashCode( )方法。例如,大多数常见的Hashtable类型使用字符串(String)对象作为关键字。String实现hashCode( )和equals( )方法。Hashtable的构造函数如下所示:
Hashtable( )
Hashtable(int size)
Hashtable(int size, float fillRatio)
Hashtable(Map m)
第一种形式是默认的构造函数。第二种形式创建一个散列表,该散列表具有由size指定的原始大小。第三种形式创建一个散列表,该散列表具有由size指定的原始大小和由fillRatio指定的填充比。填充比必须介于0.0和1.0之间,它决定了在散列表向上调整大小之前散列表的充满度。具体地说,当元素的个数大于散列表的容量乘以它的填充比时,散列表被扩展。如果没有指定填充比,默认使用0.75。最后,第四种形式创建一个散列表,该散列表用m中的元素初始化。散列表的容量被设为m中元素的个数的两倍。默认的填充因子设为0.75。第四种构造函数是在Java 2中新增加的。除了Hashtable目前实现的,由Map接口定义的方法之外,Hashtable定义的从以前版本遗留下来的方法列在表15-13中。
表15-13 由Hashtable 定义的从以前版本遗留下来的方法
方法 描述
void clear( ) 复位并清空散列表
Object clone( ) 返回调用对象的复制
boolean contains(Object value) 如果一些值与存在于散列表中的value相等的话,则返回true;如果这个值不存在,则返回false
boolean containsKey(Object key) 如果一些关键字与存在于散列表中的key相等的话,则返回true;如果这个关键字不存在,则返回false
boolean containsValue(Object value) 如果一些值与散列表中存在的value相等的话,返回true;如果这个值没有找到,则返回false(是一种为了保持一致性而在Java2中新增加的非Map方法)
Enumeration elements( ) 返回包含在散列表中的值的枚举
Object get(Object key) 返回包含与key相关联的值的对象。如果key不在散列表中,则返回一个空对象
boolean isEmpty( ) 如果散列表是空的,则返回true;如果散列表中至少包含一个关键字,则返回false
Enumeration keys( ) 返回包含在散列表中的关键字的枚举
Object put(Object key, Object value) 将关键字和值插入散列表中。如果key已经不在散列表中,返回null。如果key已经存在于散列表中,则返回与key相连的前一个值
void rehash( ) 增大散列表的大小并且对其关键字进行再散列。
Object remove(Object key) 删除key及其对应的值。返回与key相关联的值。如果key不在散列表中,则返回一个空对象
int size( ) 返回散列表中的项数
String toString( ) 返回散列表的等价字符串形式
下面的例子重写前面介绍的关于银行账目的程序。在重写的程序中,使用Hashtable储存银行存款人的名字和他们当前的资产平衡表:
// Demonstrate a Hashtable
import java.util.*;
class HTDemo {
public static void main(String args[]) {
Hashtable balance = new Hashtable();
Enumeration names;
String str;
double bal;
balance.put("John Doe", new Double(3434.34));
balance.put("Tom Smith", new Double(123.22));
balance.put("Jane Baker", new Double(1378.00));
balance.put("Todd Hall", new Double(99.22));
balance.put("Ralph Smith", new Double(-19.08));
// Show all balances in hash table.
names = balance.keys();
while(names.hasMoreElements()) {
str = (String) names.nextElement();
System.out.println(str + ": " +
balance.get(str));
}
System.out.println();
// Deposit 1,000 into John Doe's account
bal = ((Double)balance.get("John Doe")).doubleValue();
balance.put("John Doe", new Double(bal+1000));
System.out.println("John Doe's new balance: " +
balance.get("John Doe"));
}
}
该程序的输出如下所示:
Todd Hall: 99.22
Ralph Smith: -19.08
John Doe: 3434.34
Jane Baker: 1378.0
Tom Smith: 123.22
John Doe’s new balance: 4434.34
重要的一点是:和映射类一样,Hashtable不直接支持迭代函数。因此,上面的程序使用枚举来显示balance的内容。然而,我们可以获得允许使用迭代函数的散列表的集合视图。为了实现它,可以简单地使用由Map定义的一个类集“视图”方法,如entrySet( )或keySet( )方法。例如,可以获得关键字的一个集合“视图”,并遍历这些关键字。下面是采用这种技术后重新编写的程序:
// Use iterators with a Hashtable.
import java.util.*;
class HTDemo2 {
public static void main(String args[]) {
Hashtable balance = new Hashtable();
String str;
double bal;
balance.put("John Doe", new Double(3434.34));
balance.put("Tom Smith", new Double(123.22));
balance.put("Jane Baker", new Double(1378.00));
balance.put("Todd Hall", new Double(99.22));
balance.put("Ralph Smith", new Double(-19.08));
// show all balances in hashtable
Set set = balance.keySet(); // get set-view of keys
// get iterator
Iterator itr = set.iterator();
while(itr.hasNext()) {
str = (String) itr.next();
System.out.println(str + ": " +
balance.get(str));
}
System.out.println();
// Deposit 1,000 into John Doe's account
bal = ((Double)balance.get("John Doe")).doubleValue();
balance.put("John Doe", new Double(bal+1000));
System.out.println("John Doe's new balance: " +
balance.get("John Doe"));
}
}