Chinaunix首页 | 论坛 | 博客
  • 博客访问: 805492
  • 博文数量: 780
  • 博客积分: 7000
  • 博客等级: 少将
  • 技术积分: 5010
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-12 09:11
文章分类

全部博文(780)

文章存档

2011年(1)

2008年(779)

我的朋友
最近访客

分类:

2008-09-12 09:24:10

    在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呢?事实上,由于所有集合能容纳的仅有对象句柄,所以根本不可以使用整数。学过集合后,封装类的概念对大家来说就可能更容易理解了,因为不可以将任何基本数据类型置入集合里。然而,我们对封装器能做的唯一事情就是将其初始化成一个特定的值,然后读取那个值。也就是说,一旦封装器对象已经创建,就没有办法改变一个值。这使得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 + "\n");
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)。

[1]  

【责编:Ken】

--------------------next---------------------

阅读(421) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~