java的hashtable的用法

// : Statistics.java
//  Simple demonstration of Hashtable
import  java.util. * ;

class  Counter 
  
int i = 1
  
public String toString() 
    
return Integer.toString(i); 
  }

}


class  Statistics  {
  
public static void main(String[] args) {
    Hashtable ht 
= new Hashtable();
    
for(int i = 0; i < 10000; i++{
      
// Produce a number between 0 and 20:
      Integer r = 
        
new Integer((int)(Math.random() * 20));
      
if(ht.containsKey(r))
        ((Counter)ht.get(r)).i
++;
      
else
        ht.put(r, 
new Counter());
    }

    System.out.println(ht);
  }

}
  // /:~
Vector允许我们用一个数字从一系列对象中作出选择,所以它实际是将数字同对象关联起来了。但假如我们想根据其他标准选择一系列对象呢?堆栈就是这样的一个例子:它的选择标准是“最后压入堆栈的东西”。这种“从一系列对象中选择”的概念亦可叫作一个“映射”、“字典”或者“关联数组”。从概念上讲,它看起来象一个Vector,但却不是通过数字来查找对象,而是用另一个对象来查找它们!这通常都属于一个程序中的重要进程。
在Java中,这个概念具体反映到抽象类Dictionary身上。该类的接口是非常直观的size()告诉我们其中包含了多少元素;isEmpty()判断是否包含了元素(是则为true);put(Object key, Object value)添加一个值(我们希望的东西),并将其同一个键关联起来(想用于搜索它的东西);get(Object key)获得与某个键对应的值;而remove(Object Key)用于从列表中删除“键-值”对。还可以使用枚举技术:keys()产生对键的一个枚举(Enumeration);而elements()产生对所有值的一个枚举。这便是一个Dictionary(字典)的全部。
Dictionary的实现过程并不麻烦。下面列出一种简单的方法,它使用了两个Vector,一个用于容纳键,另一个用来容纳值:
// : AssocArray.java
//  Simple version of a Dictionary
import  java.util. * ;

public   class  AssocArray  extends  Dictionary  {
  
private Vector keys = new Vector();
  
private Vector values = new Vector();
  
public int size() return keys.size(); }
  
public boolean isEmpty() {
    
return keys.isEmpty();
  }

  
public Object put(Object key, Object value) {
    keys.addElement(key);
    values.addElement(value);
    
return key;
  }

  
public Object get(Object key) {
    
int index = keys.indexOf(key);
    
// indexOf() Returns -1 if key not found:
    if(index == -1return null;
    
return values.elementAt(index);
  }

  
public Object remove(Object key) {
    
int index = keys.indexOf(key);
    
if(index == -1return null;
    keys.removeElementAt(index);
    Object returnval 
= values.elementAt(index);
    values.removeElementAt(index);
    
return returnval;
  }

  
public Enumeration keys() {
    
return keys.elements();
  }

  
public Enumeration elements() {
    
return values.elements();
  }

  
// Test it:
  public static void main(String[] args) {
    AssocArray aa 
= new AssocArray();
    
for(char c = 'a'; c <= 'z'; c++)
      aa.put(String.valueOf(c),
             String.valueOf(c)
             .toUpperCase());
    
char[] ca = 'a''e''i''o''u' };
    
for(int i = 0; i < ca.length; i++)
      System.out.println(
"Uppercase: " +
             aa.get(String.valueOf(ca[i])));
  }

}
  // /:~

在对AssocArray的定义中,我们注意到的第一个问题是它“扩展”了字典。这意味着AssocArray属于Dictionary的一种类型,所以可对其发出与Dictionary一样的请求。如果想生成自己的Dictionary,而且就在这里进行,那么要做的全部事情只是填充位于Dictionary内的所有方法(而且必须覆盖所有方法,因为它们——除构建器外——都是抽象的)。
Vector key和value通过一个标准索引编号链接起来。也就是说,如果用“roof”的一个键以及“blue”的一个值调用put()——假定我们准备将一个房子的各部分与它们的油漆颜色关联起来,而且AssocArray里已有100个元素,那么“roof”就会有101个键元素,而“blue”有101个值元素。而且要注意一下get(),假如我们作为键传递“roof”,它就会产生与keys.index.Of()的索引编号,然后用那个索引编号生成相关的值矢量内的值。
main()中进行的测试是非常简单的;它只是将小写字符转换成大写字符,这显然可用更有效的方式进行。但它向我们揭示出了AssocArray的强大功能。
标准Java库只包含Dictionary的一个变种,名为Hashtable(散列表,注释③)。Java的散列表具有与AssocArray相同的接口(因为两者都是从Dictionary继承来的)。但有一个方面却反映出了差别:执行效率。若仔细想想必须为一个get()做的事情,就会发现在一个Vector里搜索键的速度要慢得多。但此时用散列表却可以加快不少速度。不必用冗长的线性搜索技术来查找一个键,而是用一个特殊的值,名为“散列码”。散列码可以获取对象中的信息,然后将其转换成那个对象“相对唯一”的整数(int)。所有对象都有一个散列码,而hashCode()是根类Object的一个方法。Hashtable获取对象的hashCode(),然后用它快速查找键。这样可使性能得到大幅度提升(④)。散列表的具体工作原理已超出了本书的范围(⑤)——大家只需要知道散列表是一种快速的“字典”(Dictionary)即可,而字典是一种非常有用的工具。

③:如计划使用RMI(在第15章详述),应注意将远程对象置入散列表时会遇到一个问题(参阅《Core Java》,作者Conrell和Horstmann,Prentice-Hall 1997年出版)
④:如这种速度的提升仍然不能满足你对性能的要求,甚至可以编写自己的散列表例程,从而进一步加快表格的检索过程。这样做可避免在与Object之间进行造型的时间延误,也可以避开由Java类库散列表例程内建的同步过程。
⑤:我的知道的最佳参考读物是《Practical Algorithms for Programmers》,作者为Andrew Binstock和John Rex,Addison-Wesley 1995年出版。

作为应用散列表的一个例子,可考虑用一个程序来检验Java的Math.random()方法的随机性到底如何。在理想情况下,它应该产生一系列完美的随机分布数字。但为了验证这一点,我们需要生成数量众多的随机数字,然后计算落在不同范围内的数字多少。散列表可以极大简化这一工作,因为它能将对象同对象关联起来(此时是将Math.random()生成的值同那些值出现的次数关联起来)。如下所示:

// : Statistics.java
//  Simple demonstration of Hashtable
import  java.util. * ;

class  Counter 
  
int i = 1
  
public String toString() 
    
return Integer.toString(i); 
  }

}


class  Statistics  {
  
public static void main(String[] args) {
    Hashtable ht 
= new Hashtable();
    
for(int i = 0; i < 10000; i++{
      
// Produce a number between 0 and 20:
      Integer r = 
        
new Integer((int)(Math.random() * 20));
      
if(ht.containsKey(r))
        ((Counter)ht.get(r)).i
++;
      
else
        ht.put(r, 
new Counter());
    }

    System.out.println(ht);
  }

}
  // /:~

 

在main()中,每次产生一个随机数字,它都会封装到一个Integer对象里,使句柄能够随同散列表一起使用(不可对一个集合使用基本数据类型,只能使用对象句柄)。containKey()方法检查这个键是否已经在集合里(也就是说,那个数字以前发现过吗?)若已在集合里,则get()方法获得那个键关联的值,此时是一个Counter(计数器)对象。计数器内的值i随后会增加1,表明这个特定的随机数字又出现了一次。
假如键以前尚未发现过,那么方法put()仍然会在散列表内置入一个新的“键-值”对。在创建之初,Counter会自己的变量i自动初始化为1,它标志着该随机数字的第一次出现。
为显示散列表,只需把它简单地打印出来即可。Hashtable toString()方法能遍历所有键-值对,并为每一对都调用toString()。Integer toString()是事先定义好的,可看到计数器使用的toString。一次运行的结果(添加了一些换行)如下:

{19=526, 18=533, 17=460, 16=513, 15=521, 14=495, 13=512, 12=483, 11=488, 10=487, 9=514, 8=523, 7=497, 6=487, 5=480, 4=489, 3=509, 2=503, 1=475, 0=505}

大家或许会对Counter类是否必要感到疑惑,它看起来似乎根本没有封装类Integer的功能。为什么不用int或Integer呢?事实上,由于所有集合能容纳的仅有对象句柄,所以根本不可以使用整数。学过集合后,封装类的概念对大家来说就可能更容易理解了,因为不可以将任何基本数据类型置入集合里。然而,我们对Java封装器能做的唯一事情就是将其初始化成一个特定的值,然后读取那个值。也就是说,一旦封装器对象已经创建,就没有办法改变一个值。这使得Integer封装器对解决我们的问题毫无意义,所以不得不创建一个新类,用它来满足自己的要求。

1. 创建“关键”类
在前面的例子里,我们用一个标准库的类(Integer)作为Hashtable的一个键使用。作为一个键,它能很好地工作,因为它已经具备正确运行的所有条件。但在使用散列表的时候,一旦我们创建自己的类作为键使用,就会遇到一个很常见的问题。例如,假设一套天气预报系统将Groundhog(土拔鼠)对象匹配成Prediction(预报)。这看起来非常直观:我们创建两个类,然后将Groundhog作为键使用,而将Prediction作为值使用。如下所示:

// : SpringDetector.java
//  Looks plausible, but doesn't work right.
import  java.util. * ;

class  Groundhog  {
  
int ghNumber;
  Groundhog(
int n) { ghNumber = n; }
}


class  Prediction  {
  
boolean shadow = Math.random() > 0.5;
  
public String toString() {
    
if(shadow)
      
return "Six more weeks of Winter!";
    
else
      
return "Early Spring!";
  }

}


public   class  SpringDetector  {
  
public static void main(String[] args) {
    Hashtable ht 
= new Hashtable();
    
for(int i = 0; i < 10; i++)
      ht.put(
new Groundhog(i), new Prediction());
    System.out.println(
"ht = " + ht + " ");
    System.out.println(
      
"Looking up prediction for groundhog #3:");
    Groundhog gh 
= new Groundhog(3);
    
if(ht.containsKey(gh))
      System.out.println((Prediction)ht.get(gh));
  }

}
  // /:~
每个Groundhog都具有一个标识号码,所以赤了在散列表中查找一个Prediction,只需指示它“告诉我与Groundhog号码3相关的Prediction”。Prediction类包含了一个布尔值,用Math.random()进行初始化,以及一个toString()为我们解释结果。在main()中,用Groundhog以及与它们相关的Prediction填充一个散列表。散列表被打印出来,以便我们看到它们确实已被填充。随后,用标识号码为3的一个Groundhog查找与Groundhog #3对应的预报。
看起来似乎非常简单,但实际是不可行的。问题在于Groundhog是从通用的Object根类继承的(若当初未指定基础类,则所有类最终都是从Object继承的)。事实上是用Object的hashCode()方法生成每个对象的散列码,而且默认情况下只使用它的对象的地址。所以,Groundhog(3)的第一个实例并不会产生与Groundhog(3)第二个实例相等的散列码,而我们用第二个实例进行检索。
大家或许认为此时要做的全部事情就是正确地覆盖hashCode()。但这样做依然行不能,除非再做另一件事情:覆盖也属于Object一部分的equals()。当散列表试图判断我们的键是否等于表内的某个键时,就会用到这个方法。同样地,默认的Object.equals()只是简单地比较对象地址,所以一个Groundhog(3)并不等于另一个Groundhog(3)。
因此,为了在散列表中将自己的类作为键使用,必须同时覆盖hashCode()和equals(),就象下面展示的那样:
// : SpringDetector2.java
//  If you create a class that's used as a key in
//  a Hashtable, you must override hashCode()
//  and equals().
import  java.util. * ;

class  Groundhog2  {
  
int ghNumber;
  Groundhog2(
int n) { ghNumber = n; }
  
public int hashCode() return ghNumber; }
  
public boolean equals(Object o) {
    
return (o instanceof Groundhog2)
      
&& (ghNumber == ((Groundhog2)o).ghNumber);
  }

}


public   class  SpringDetector2  {
  
public static void main(String[] args) {
    Hashtable ht 
= new Hashtable();
    
for(int i = 0; i < 10; i++)
      ht.put(
new Groundhog2(i),new Prediction());
    System.out.println(
"ht = " + ht + " ");
    System.out.println(
      
"Looking up prediction for groundhog #3:");
    Groundhog2 gh 
= new Groundhog2(3);
    
if(ht.containsKey(gh))
      System.out.println((Prediction)ht.get(gh));
  }

}
  // /:~
注意这段代码使用了来自前一个例子的Prediction,所以SpringDetector.java必须首先编译,否则就会在试图编译SpringDetector2.java时得到一个编译期错误。
Groundhog2.hashCode()将土拔鼠号码作为一个标识符返回(在这个例子中,程序员需要保证没有两个土拔鼠用同样的ID号码并存)。为了返回一个独一无二的标识符,并不需要hashCode(),equals()方法必须能够严格判断两个对象是否相等。
equals()方法要进行两种检查:检查对象是否为null;若不为null,则继续检查是否为Groundhog2的一个实例(要用到instanceof关键字,第11章会详加论述)。即使为了继续执行equals(),它也应该是一个Groundhog2。正如大家看到的那样,这种比较建立在实际ghNumber的基础上。这一次一旦我们运行程序,就会看到它终于产生了正确的输出(许多Java库的类都覆盖了hashcode()和equals()方法,以便与自己提供的内容适应)。

2. 属性:Hashtable的一种类型
在本书的第一个例子中,我们使用了一个名为Properties(属性)的Hashtable类型。在那个例子中,下述程序行:
Properties p = System.getProperties();
p.list(System.out);
调用了一个名为getProperties()的static方法,用于获得一个特殊的Properties对象,对系统的某些特征进行描述。list()属于Properties的一个方法,可将内容发给我们选择的任何流式输出。也有一个save()方法,可用它将属性列表写入一个文件,以便日后用load()方法读取。
尽管Properties类是从Hashtable继承的,但它也包含了一个散列表,用于容纳“默认”属性的列表。所以假如没有在主列表里找到一个属性,就会自动搜索默认属性。
Properties类亦可在我们的程序中使用(第17章的ClassScanner.java便是一例)。在Java库的用户文档中,往往可以找到更多、更详细的说明。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java中的Hashtable是一种基于哈希表的数据结构,用于存储键值对。它的使用方法如下: 1. 创建Hashtable对象: Hashtable<String, Integer> hashtable = new Hashtable<String, Integer>(); 2. 添加键值对: hashtable.put("key1", 1); hashtable.put("key2", 2); hashtable.put("key3", 3); 3. 获取值: int value = hashtable.get("key1"); 4. 判断是否包含某个键: boolean containsKey = hashtable.containsKey("key1"); 5. 判断是否包含某个值: boolean containsValue = hashtable.containsValue(1); 6. 删除键值对: hashtable.remove("key1"); 7. 获取所有键: Enumeration<String> keys = hashtable.keys(); 8. 获取所有值: Enumeration<Integer> values = hashtable.elements(); 注意:Hashtable是线程安全的,但是在多线程环境下,使用ConcurrentHashMap会更好。 ### 回答2: Java Hashtable是一个非常实用的数据结构,因为它可以将键和值的对映射起来,快速地在哈希表中查找和存储数据。要在Java中使用Hashtable,需要知道以下几个方面: 1. 创建Hashtable对象 要创建一个Hashtable对象,应该在Java中使用关键字“new”和Hashtable类名称,并将它们作为构造函数的参数。例如,要创建一个Hashtable对象,代码如下所示: Hashtable<String, String> hashtable = new Hashtable<String, String>(); 注意,这个语句通过使用“String”类型的键和值来创建了一个Hashtable对象。如果不指定键和值的类型,就会得到一个“Object”类型的Hashtable,这可能会导致类型转换问题。 2. 添加键和值 要在Hashtable中添加一个键和值,可以使用“put”方法。这个方法需要一个键和一个值作为参数,并将它们添加到Hashtable中。例如: hashtable.put("key1", "value1"); hashtable.put("key2", "value2"); hashtable.put("key3", "value3"); 3. 获取值 使用“get”方法可以获取Hashtable中的值。这个方法需要一个键作为参数,并返回与该键相关联的值。例如: String value1 = hashtable.get("key1"); String value2 = hashtable.get("key2"); String value3 = hashtable.get("key3"); 4. 删除键和值 要从Hashtable中删除一个键和值,可以使用“remove”方法。这个方法需要一个键作为参数,并删除与该键相关联的键和值。例如: hashtable.remove("key1"); 5. 遍历Hashtable 要遍历Hashtable中的键和值,可以使用“entrySet”和“for”循环。例如: Set<Entry<String, String>> set = hashtable.entrySet(); for (Entry<String, String> entry : set) { String key = entry.getKey(); String value = entry.getValue(); System.out.println("Key: " + key + ", Value: " + value); } 在上面的代码中,首先使用“entrySet”方法获取Hashtable中的所有键和值的集合。然后,循环遍历集合,并使用“getKey”和“getValue”方法获取每个键和值。 总之,Java Hashtable是一个非常实用的数据结构,可以快速地在哈希表中查找和存储数据。要使用Hashtable,需要创建Hashtable对象、添加键和值、获取值、删除键和值以及遍历Hashtable。 ### 回答3: Java Hashtable 是一个基于哈希表实现的 Map 接口,它可用于存储 key-value 键值对,其中每个键都是唯一的。与 HashMap 不同,Hashtable 是线程安全的,它的所有方法都是同步的,因此可以在多线程环境下使用,并且在性能方面与 HashMap 相近。 Java Hashtable 的基本用法如下: 1. 创建 Hashtable 对象: Hashtable<String, String> hashtable = new Hashtable<>(); 其中,"<String, String>" 表示键和值都是字符串类型,也可以使用其他数据类型,如 Integer、Double 等。 2. 添加元素: 添加元素可以使用 put() 方法: hashtable.put("key", "value"); 其中,"key" 表示键,"value" 表示值。 3. 获取元素: 获取元素可以使用 get() 方法: String value = hashtable.get("key"); 其中,"key" 表示键,"value" 表示值。 4. 删除元素: 删除元素可以使用 remove() 方法: hashtable.remove("key"); 其中,"key" 表示键。 5. 遍历元素: 遍历元素可以使用 Enumeration 或者 Iterator: (1)使用 Enumeration 遍历: Enumeration<String> keys = hashtable.keys(); while (keys.hasMoreElements()) { String key = keys.nextElement(); String value = hashtable.get(key); } 其中,keys() 方法返回了一个键的 Enumeration 对象,可以使用 hasMoreElements() 和 nextElement() 方法遍历所有键值对。 (2)使用 Iterator 遍历: Iterator<Map.Entry<String, String>> iterator = hashtable.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, String> entry = iterator.next(); String key = entry.getKey(); String value = entry.getValue(); } 其中,entrySet() 方法返回了一个包含所有键值对的 Set 对象,可以使用 iterator() 方法返回一个迭代器,然后使用 hasNext() 和 next() 方法遍历所有键值对。 以上就是 Java Hashtable 的基本用法,除了以上方法之外,Hashtable 还包含了其它一些重要的方法,如 contains、size() 等,可以根据实际需要使用。注意,在多线程环境下使用 Hashtable 时,需要使用 synchronized 或者 ConcurrentHashMap 等线程安全的集合类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值