八宝书库 > 文学其他电子书 > Java编程思想第4版[中文版](PDF格式) >

第62部分

Java编程思想第4版[中文版](PDF格式)-第62部分

小说: Java编程思想第4版[中文版](PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!





…………………………………………………………Page 219……………………………………………………………

样一个奇怪的名字。Java 1。2 的集合库修正了这个问题以及其他许多问题。  

  

只可用Enumeration 做这些事情,不能再有更多。它属于反复器一种简单的实现方式,但功能依然十分强 

大。为体会它的运作过程,让我们复习一下本章早些时候提到的CatsAndDogs。java 程序。在原始版本中, 

elementAt()方法用于选择每一个元素,但在下述修订版中,可看到使用了一个“枚举”:  

  

//: CatsAndDogs2。java  

// Simple collection with Enumeration  

import java。util。*;  

  

class Cat2 {  

  private int catNumber;  

  Cat2(int i) {  

    catNumber = i;  

  }  

  void print() {  

    System。out。println(〃Cat number 〃 +catNumber);  

  }  

}  

  

class Dog2 {  

  private int dogNumber;  

  Dog2(int i) {  

    dogNumber = i;  

  }  

  void print() {  

    System。out。println(〃Dog number 〃 +dogNumber);  

  }  

}  

  

public class CatsAndDogs2 {  

  public static void main(String'' args) {  

    Vector cats = new Vector();  

    for(int i = 0; i 《 7; i++)  

      cats。addElement(new Cat2(i));  

    // Not a problem to add a dog to cats:  

    cats。addElement(new Dog2(7));  

    Enumeration e = cats。elements();  

    while(e。hasMoreElements())  

      ((Cat2)e。nextElement())。print();  

    // Dog is detected only at run…time  

  }  

} ///:~  

  

我们看到唯一的改变就是最后几行。不再是:  

  

for(int i = 0; i 《 cats。size(); i++)  

((Cat)cats。elementAt(i))。print();  

  

而是用一个 Enumeration 遍历整个序列:  

  

while(e。hasMoreElements())  



                                                                                             218 


…………………………………………………………Page 220……………………………………………………………

((Cat2)e。nextElement())。print();  

  

使用Enumeration,我们不必关心集合中的元素数量。所有工作均由 hasMoreElements()和nextElement()自 

动照管了。  

下面再看看另一个例子,让我们创建一个常规用途的打印方法:  

  

//: HamsterMaze。java  

// Using an Enumeration  

import java。util。*;  

  

class Hamster {  

  private int hamsterNumber;  

  Hamster(int i) {  

    hamsterNumber = i;  

  }  

  public String toString() {  

    return 〃This is Hamster #〃 + hamsterNumber;  

  }  

}  

  

class Printer {  

  static void printAll(Enumeration e) {  

    while(e。hasMoreElements())  

      System。out。println(  

        e。nextElement()。toString());  

  }  

}  

  

public class HamsterMaze {  

  public static void main(String'' args) {  

    Vector v = new Vector();  

    for(int i = 0; i 《 3; i++)  

      v。addElement(new Hamster(i));  

    Printer。printAll(v。elements());  

  }  

} ///:~  

  

仔细研究一下打印方法:  

  

static void printAll(Enumeration e) {  

  while(e。hasMoreElements())  

    System。out。println(  

      e。nextElement()。toString());  

}  

  

注意其中没有与序列类型有关的信息。我们拥有的全部东西便是Enumeration。为了解有关序列的情况,一 

个Enumeration 便足够了:可取得下一个对象,亦可知道是否已抵达了末尾。取得一系列对象,然后在其中 

遍历,从而执行一个特定的操作——这是一个颇有价值的编程概念,本书许多地方都会沿用这一思路。  

这个看似特殊的例子甚至可以更为通用,因为它使用了常规的 toString()方法(之所以称为常规,是由于它 

属于Object 类的一部分)。下面是调用打印的另一个方法(尽管在效率上可能会差一些):  

System。out。println(〃〃 + e。nextElement());  

它采用了封装到Java 内部的“自动转换成字串”技术。一旦编译器碰到一个字串,后面跟随一个“+”,就 



                                                                                             219 


…………………………………………………………Page 221……………………………………………………………

会希望后面又跟随一个字串,并自动调用 toString()。在Java 1。1 中,第一个字串是不必要的;所有对象 

都会转换成字串。亦可对此执行一次造型,获得与调用toString()同样的效果:  

System。out。println((String)e。nextElement())  

但我们想做的事情通常并不仅仅是调用Object 方法,所以会再度面临类型造型的问题。对于自己感兴趣的类 

型,必须假定自己已获得了一个Enumeration,然后将结果对象造型成为那种类型(若操作错误,会得到运 

行期违例)。  



8。4 集合的类型  



标准Java 1。0 和 1。1库配套提供了非常少的一系列集合类。但对于自己的大多数编程要求,它们基本上都能 

胜任。正如大家到本章末尾会看到的,Java 1。2 提供的是一套重新设计过的大型集合库。  



8。4。1 Vector  



Vector 的用法很简单,这已在前面的例子中得到了证明。尽管我们大多数时候只需用addElement()插入对 

象,用 elementAt()一次提取一个对象,并用elements()获得对序列的一个“枚举”。但仍有其他一系列方 

法是非常有用的。同我们对于 Java 库惯常的做法一样,在这里并不使用或讲述所有这些方法。但请务必阅读 

相应的电子文档,对它们的工作有一个大概的认识。  

  

1。 崩溃Java  

Java 标准集合里包含了 toString()方法,所以它们能生成自己的 String 表达方式,包括它们容纳的对象。 

例如在Vector 中,toString()会在Vector 的各个元素中步进和遍历,并为每个元素调用 toString()。假定 

我们现在想打印出自己类的地址。看起来似乎简单地引用 this 即可(特别是C++程序员有这样做的倾向):  

  

//: CrashJava。java  

// One way to crash Java  

import java。util。*;  

  

public class CrashJava {  

  public String toString() {  

    return 〃CrashJava address: 〃 + this + 〃n〃;  

  }  

  public static void main(String'' args) {  

    Vector v = new Vector();  

    for(int i = 0; i 《 10; i++)  

      v。addElement(new CrashJava());  

    System。out。println(v);  

  }  

} ///:~  

  

若只是简单地创建一个 CrashJava 对象,并将其打印出来,就会得到无穷无尽的一系列违例错误。然而,假 

如将CrashJava 对象置入一个Vector,并象这里演示的那样打印Vector,就不会出现什么错误提示,甚至连 

一个违例都不会出现。此时Java 只是简单地崩溃(但至少它没有崩溃我的操作系统)。这已在 Java 1。1 中 

测试通过。  

此时发生的是字串的自动类型转换。当我们使用下述语句时:  

〃CrashJava address: 〃 + this  

编译器就在一个字串后面发现了一个“+”以及好象并非字串的其他东西,所以它会试图将 this 转换成一个 

字串。转换时调用的是 toString(),后者会产生一个递归调用。若在一个Vector 内出现这种事情,看起来 

堆栈就会溢出,同时违例控制机制根本没有机会作出响应。  

若确实想在这种情况下打印出对象的地址,解决方案就是调用 Object 的 toString 方法。此时就不必加入 

this,只需使用 super。toString()。当然,采取这种做法也有一个前提:我们必须从 Object 直接继承,或 

者没有一个父类覆盖了 toString 方法。  



                                                                                  220 


…………………………………………………………Page 222……………………………………………………………

8。4。2 BitSet  



BitSet 实际是由“二进制位”构成的一个 Vector。如果希望高效率地保存大量“开-关”信息,就应使用 

BitSet。它只有从尺寸的角度看才有意义;如果希望的高效率的访问,那么它的速度会比使用一些固有类型 

的数组慢一些。  

此外,BitSet 的最小长度是一个长整数(Long )的长度:64 位。这意味着假如我们准备保存比这更小的数 

据,如 8 位数据,那么 BitSet 就显得浪费了。所以最好创建自己的类,用它容纳自己的标志位。  

在一个普通的Vector 中,随我们加入越来越多的元素,集合也会自我膨胀。在某种程度上,BitSet 也不例 

外。也就是说,它有时会自行扩展,有时则不然。而且Java 的1。0 版本似乎在这方面做得最糟,它的 

BitSet 表现十分差强人意(Java1。1 已改正了这个问题)。下面这个例子展示了BitSet 是如何运作的,同时 

演示了 1。0 版本的错误:  

  

//: Bits。java  

// Demonstration of BitSet  

import java。util。*;  

  

public class Bits {  

  public static void main(String'' args) {  

    Random rand = new Random();  

    // Take the LSB of nextInt():  

    byte bt = (byte)rand。nextInt();  

    BitSet bb = new BitSet();  

    for(int i = 7; i 》=0; i……)  

      if(((1 =0; i……)  

      if(((1 =0; i……)  

      if(((1 = 64 bits:  

    BitSet b127 = new BitSet();  

    b127。set(127);  



                                                                                          221 


…………………………………………………………Page 223……………………………………………………………

    System。out。println(〃set bit 127: 〃 + b127);  

    BitSet b255 = new BitSet(65);  

    b255。set(255);  

    System。out。println(〃set bit 255: 〃 + b255);  

    BitSet b1023 = new BitSet(512);  

// Without the following; an exception is thrown  

// in the Java 1。0 implementation of BitSet:  

//    b1023。set(1023);  

    b1023。set(1024);  

    System。out。println(〃set bit 1023: 〃 + b1023);  

  }  

  static void printBitSet(BitSet b) {  

    System。out。println(〃bits: 〃 + b);  

    String bbits = new String();  

    for(int j = 0; j 《 b。size() ; j++)  

      bbits += (b。get(j) ? 〃1〃 : 〃0〃);  

    System。out。println(〃bit pattern: 〃 + bbits);  

  }  

} ///:~  

  

随机数字生成器用于创建一个随机的byte、short 和 int。每一个都会转换成BitSet 内相应的位模型。此时 

一切都很正常,因为BitSet 是64 位的,所以它们都不会造成最终尺寸的增大。但在 Java 1。0 中,一旦 

BitSet 大于64 位,就会出现一些令人迷惑不解的行为。假如我们设置一个只比BitSet 当前分配存储空间大 

出1 的一个位,它能够正常地扩展。但一旦试图在更高的位置设置位,同时不先接触边界,就会得到一个恼 

人的违例。这正是由于 BitSet 在 Java 1。0 里不能正确扩展造成的。本例创建了一个 512 位的BitSet。构建 

器分配的存储空间是位数的两倍。所以假如设置位 1024 或更高的位,同时没有先设置位 1023,就会在Java  

1。0里得到一个违例。但幸运的是,这个问题已在 Java 1。1 得到了改正。所以如果是为Java 1。0 写代码, 

请尽量避免使用BitSet。  



8。4。3 Stack  



Stack 有时也可以称为“后入先出”(LIFO )集合。换言之,我们在堆栈里最后“压入”的东西将是以后第 


返回目录 上一页 下一页 回到顶部 0 1

你可能喜欢的