友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
富士康小说网 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

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

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!



是在Object 里定义它)。  

针对Object 数组(以及 String,它当然属于Object 的一种),可使用一个 sort(),并令其接纳另一个参 

数:实现了 parator 接口(即“比较器”接口,新集合库的一部分)的一个对象,并用它的单个 

pare()方法进行比较。这个方法将两个准备比较的对象作为自己的参数使用——若第一个参数小于第二 

个,返回一个负整数;若相等,返回零;若第一个参数大于第二个,则返回正整数。基于这一规则,上述例 

子的String 部分便可重新写过,令其进行真正按字母顺序的排序:  

  

//: Alphap。java  

// Using parator to perform an alphabetic sort  

package c08。newcollections;  

import java。util。*;  

  

public class Alphap implements parator {  

  public int pare(Object o1; Object o2) {  

    // Assume it's used only for Strings。。。  

    String s1 = ((String)o1)。toLowerCase();  

    String s2 = ((String)o2)。toLowerCase();  

    return s1。pareTo(s2);  

  }  

  public static void main(String'' args) {  

    String'' s = Array1。randStrings(4; 10);  

    Array1。print(s);  

    Alphap ac = new Alphap();  

    Arrays。sort(s; ac);  

    Array1。print(s);  

    // Must use the parator to search; also:  

    int loc = Arrays。binarySearch(s; s'3'; ac);  

    System。out。println(〃Location of 〃 + s'3' +  

     〃 = 〃 + loc);  

  }  

} ///:~  

  

通过造型为 String,pare()方法会进行“暗示”性的测试,保证自己操作的只能是String 对象——运行 

期系统会捕获任何差错。将两个字串都强迫换成小写形式后,String。pareTo()方法会产生预期的结果。  

若用自己的 parator 来进行一次 sort(),那么在使用binarySearch()时必须使用那个相同的 

parator。  

Arrays 类提供了另一个 sort()方法,它会采用单个自变量:一个 Object 数组,但没有parator。这个 

sort()方法也必须用同样的方式来比较两个 Object。通过实现parable 接口,它采用了赋予一个类的 

 “自然比较方法”。这个接口含有单独一个方法——pareTo(),能分别根据它小于、等于或者大于自变量 

而返回负数、零或者正数,从而实现对象的比较。下面这个例子简单地阐示了这一点:  

  

//: pClass。java  

// A class that implements parable  

package c08。newcollections;  

import java。util。*;  

  

public class pClass implements parable {  

  private int i;  

  public pClass(int ii) { i = ii; }  

  public int pareTo(Object o) {  

    // Implicitly tests for correct type:  



                                                                                          257 


…………………………………………………………Page 259……………………………………………………………

    int argi = ((pClass)o)。i;  

    if(i == argi) return 0;  

    if(i 《 argi) return …1;  

    return 1;  

  }  

  public static void print(Object'' a) {  

    for(int i = 0; i 《 a。length; i++)  

      System。out。print(a'i' + 〃 〃);  

    System。out。println();  

  }  

  public String toString() { return i + 〃〃; }  

  public static void main(String'' args) {  

    pClass'' a = new pClass'20';  

    for(int i = 0; i 《 a。length; i++)  

      a'i' = new pClass(  

        (int)(Math。random() *100));  

    print(a);  

    Arrays。sort(a);  

    print(a);  

    int loc = Arrays。binarySearch(a; a'3');  

    System。out。println(〃Location of 〃 + a'3' +  

     〃 = 〃 + loc);  

  }  

} ///:~  

  

当然,我们的 pareTo()方法亦可根据实际情况增大复杂程度。  

  

3。 列表  

可用与数组相同的形式排序和搜索一个列表(List )。用于排序和搜索列表的静态方法包含在类 

Collections 中,但它们拥有与Arrays 中差不多的签名:sort(List)用于对一个实现了 parable 的对象 

列表进行排序;binarySearch(List;Object)用于查找列表中的某个对象;sort(List;parator)利用一个 

 “比较器”对一个列表进行排序;而binarySearch(List;Object;parator)则用于查找那个列表中的一个 

对象(注释⑨)。下面这个例子利用了预先定义好的pClass 和 Alphap 来示范 Collections 中的各种 

排序工具:  

  

//: ListSort。java  

// Sorting and searching Lists with 'Collections'  

package c08。newcollections;  

import java。util。*;  

  

public class ListSort {  

  public static void main(String'' args) {   

    final int SZ = 20;  

    // Using 〃natural parison method〃:  

    List a = new ArrayList();  

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

      a。add(new pClass(  

        (int)(Math。random() *100)));  

    Collection1。print(a);  

    Collections。sort(a);  

    Collection1。print(a);  

    Object find = a。get(SZ/2);  



                                                                                          258 


…………………………………………………………Page 260……………………………………………………………

    int loc = Collections。binarySearch(a; find);  

    System。out。println(〃Location of 〃 + find +  

     〃 = 〃 + loc);  

    // Using a parator:  

    List b = new ArrayList();  

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

      b。add(Array1。randString(4));  

    Collection1。print(b);  

    Alphap ac = new Alphap();  

    Collections。sort(b; ac);  

    Collection1。print(b);  

    find = b。get(SZ/2);  

    // Must use the parator to search; also:  

    loc = Collections。binarySearch(b; find; ac);  

    System。out。println(〃Location of 〃 + find +  

     〃 = 〃 + loc);  

  }  

} ///:~  

  

⑨:在本书写作时,已宣布了一个新的Collections。stableSort(),可用它进行合并式排序,但还没有它的 

测试版问世。  

  

这些方法的用法与在Arrays 中的用法是完全一致的,只是用一个列表代替了数组。  

TreeMap 也必须根据 parable 或者parator 对自己的对象进行排序。  



8。7。8  实用工具  



Collections 类中含有其他大量有用的实用工具:  

  



enumeration(Collection) Produces an old…style E n u m e r a t i o n  for the argument。  



m a x ( C o l l e c t i o n )     Produces the maximum or minimum element in the argument using the  

m i n ( C o l l e c t i o n )   natural parison method of the objects in the C o l l e c t i o n 。  



m a x ( C o l l e c t i o n ;   Produces the maximum or minimum element in the Collection using the  

C o m p a r a t o r )         C o m p a r a t o r 。   

m i n ( C o l l e c t i o n ;   

C o m p a r a t o r )   



nCopies(int n; Object o) Returns an immutable L i s t of size n  whose handles all point to o。  



subList(List; int min; int Returns a new L i s t  backed by the specified argument List  that is a  

m a x )                       window into that argument with indexes starting at min  and stopping  

                              just before max 。   



  

enumeration(Collection) 为自变量产生原始风格的Enumeration (枚举)  

max(Collection),min(Collection) 在自变量中用集合内对象的自然比较方法产生最大或最小元素  

max(Collection;parator),min(Collection;parator) 在集合内用比较器产生最大或最小元素  

nCopies(int n; Object o) 返回长度为 n 的一个不可变列表,它的所有句柄均指向o  

subList(List;int min;int max) 返回由指定参数列表后推得到的一个新列表。可将这个列表想象成一个 

 “窗口”,它自索引为min 的地方开始,正好结束于max 的前面  

  

注意min()和max()都是随同Collection 对象工作的,而非随同 List,所以不必担心Collection 是否需要 

排序(就象早先指出的那样,在执行一次 binarySearch()——即二进制搜索——之前,必须对一个 List 或 

者一个数组执行 sort())。  



                                                                                                   259 


…………………………………………………………Page 261……………………………………………………………

  

1。 使 Collection 或 Map 不可修改  

通常,创建 Collection 或 Map 的一个“只读”版本显得更有利一些。Collections 类允许我们达到这个目 

标,方法是将原始容器传递进入一个方法,并令其传回一个只读版本。这个方法共有四种变化形式,分别用 

于Collection (如果不想把集合当作一种更特殊的类型对待)、List、Set 以及Map 。下面这个例子演示了 

为它们分别构建只读版本的正确方法:  

  

//: ReadOnly。java  

// Using the Collections。unmodifiable methods  

package c08。newcollections;  

import java。util。*;  

  

public class ReadOnly {  

  public static void main(String'' args) {  

    Collection c = new ArrayList();  

    Collection1。fill(c); // Insert useful data  

    c = Collections。unmodifiableCollection(c);  

    Collection1。print(c); // Reading is OK  

    //! c。add(〃one〃); // Can't change it  

      

    List a = new ArrayList();  

    Collection1。fill(a);  

    a = Collections。unmodifiableList(a);  

    ListIterator lit = a。listIterator();  

    System。out。println(lit。next()); // Reading OK  

    //! lit。add(〃one〃); // Can't change it  

  

    Set s = new HashSet();  

    Collection1。fill(s);  

    s = Collections。unmodifiableSet(s);  

    Collection1。print(s); // Reading OK  

    //! s。add(〃one〃); // Can't change it  

      

    Map m = new HashMap();  

    Map1。fill(m; Map1。testData1);  

    m = Collections。unmodifiableMap(m);  

    Map1。print(m); // Reading OK  

    //! m。put(〃Ralph〃; 〃Howdy!〃);  

  }  

} ///:~  

  

对于每种情况,在将其正式变为只读以前,都必须用有有效的数据填充容器。一旦载入成功,最佳的做法就 

是用“不可修改”调用产生的句柄替换现有的句柄。这样做可有效避免将其变成不可修改后不慎改变其中的 

内容。在另一方面,该工具也允许我们在一个类中将能够修改的容器保持为private 状态,并可从一个方法 

调用中返回指向那个容器的一个只读句柄。这样一来,虽然我们可在类里修改它,但其他任何人都只能读。  

为特定类型调用“不可修改”的方法不会造成编译期间的检查,但一旦发生任何变化,对修改特定容器的方 

法的调用便会产生一个 UnsupportedOperationException 违例。  

  

2。 Collection 或Map 的同步  

synchronized关键字是“多线程”机制一个非常重要的部分。我们到第 14章才会对这一机制作深入的探 

讨。在这儿,大家只需注意到 Collections 类提供了对整个容器进行自动同步的一种途径。它的语法与“不 

可修改”的方法是类似的:  



                                                                                          260 


…………………………………………………………Page 262……………………………………………………………

  

//: Synchronization。java  

// Using the Collections。synchronized methods  

package c08。newcollections;  

import java。util。*;  

  

public class Synchronization {  

  public static void main(String'' args) {  

    Collection c =   

      Collections。synchronizedCollection(  

        new ArrayList());  

    List list = Collections。synchronizedList(  

      new ArrayList());  

    Set s = Collections。synchronizedSet(  

      new HashSet());  

    Map m = Collections。synchronizedMap(  

      new HashMap());  

  }  

} ///:~  

  

在这种情况下,我们通过适当的“同步”方法直接传递新容器;这样做可避免不慎暴露出未同步的版本。  

新集合也提供了能防止多个进程同时修改一个容器内容的机制。若在一个容器里反复,同时另一些进程介 

入,并在那个容器中插入、删除或修改一个对象,便会面临发生冲突的危险。我们可能已传递了那个对象, 

可能它位位于我们前面,可能容器的大小在我们调用size()后已发生了收缩——我们面临各种各样可能的危 

险。针对这个问题,新的集合库集成了一套解决的机制,能查出除我们的进程自己需要负责的之外的、对容 

器的其他任何修改。若探测到有其他方面也准备修改容器,便会立即产生一个 

ConcurrentModificationException (并发修改违例)。我们将这一机制称为“立即失败”——它并不用更复 

杂的算法在“以后”侦测问题,而是“立即”产生违例。  



8。8 总结  



下面复习一下由标准Java (1。0和 1。1)库提供的集合(BitSet 未包括在这里,因为它更象一种负有特殊使 

命的类):  

(1) 数组包含了对象的数字化索引。它容纳的是一种已知类型的对象,所以在查找一个对象时,不必对结果 

进行造型处理。数组可以是多维的,而且能够容纳基本数据类型。但是,一旦把它创建好以后,大小便不能 

变化了。  

(2) Vector (矢量)也包含了对象的数字索引——可将数组和 Vector 想象成随机访问集合。当我们加入更多 

的元素时,Vector 能够自动改变自身的大小。但 Vector 只能容纳对象的句柄,所以它不可包含基本数据类 

型;而且将一个对象句柄从集合中取出来的时候,必须对结果进行造型处理。  

(3) Hashtable (散列表)属于Dictionary (字典)的一种类型,是一种将对象(而不是数字)同其他对象 

关联到一起的方式。散列表也支持对对象的随机访问,事实上,它的整个设计方案都在突出访问的“高速 

度”。  

(4) Stack (堆栈)是一种“后入先出”(LIFO )的队列。  

  

若你曾经熟悉数据结构,可能会疑惑为何没看到一套更大的集合。从功能的角度出发,你真的需要一套更大 

的集合吗?对于Hashtable,可将任何东西置入其中,并以非常快的速度检索;对于 Enumeration (枚举), 

可遍历一个序列,并对其中的每个元素都采取一个特定的操作。那是一种功能足够强劲的工具。  

但Hashtable 没有“顺序”的概念。Vector 和数组为我们提供了一种线性顺序,但若要把一个元素插入它们 

任何一个的中部,一般都要付出“惨重”的代价。除此以外,队列、拆散队列、优先级队列以及树都涉及到 

元素的“排序”——并非仅仅将它们置入,以便以后能按线性顺序查找或移动它们。这些数据结构也非常有 

用,这也正是标准 C++中包含了它们的原因。考虑到这个原因,只应将标准Java 库的集合看作自己的一个起 

点。而且倘若必须使用 Java 1。0 或 1。1,则可在需要超越它们的时候使用JGL。  

如果能使用 Java 1。2,那么只使用新集合即可,它一般能满足我们的所有需要。注意本书在Java 1。1 身上 



                                                                               261 


…………………………………………………………Page 263……………………………………………………………

花了大量篇幅,所以书中用到的大量集合都是只能在Java1。1 中用到的那些:Vector 和 Hashtable。就目前 

来看,这是一个不得以而为之的做法。但是,这样处理亦可提供与老 Java 代码更出色的向后兼容能力。若要 

用Java1。2 写新代码,新的集合往往能更好地为你服务。  



8。9 练习  



(1) 新建一个名为Gerbil 的类,在构建器中初始化一个 int gerbilNumber (类似本章的Mouse 例子)。为 

其写一个名为hop()的方法,用它打印出符合hop()条件的 Gerbil 的编号。建一个Vector,并为Vector 添 

加一系列Gerbil 对象。现在,用elementAt()方法在 Vector 中遍历,并为每个Gerbil 都调用 hop()。  

(2) 修改练习 1,用Enumeration 在调用 hop()的同时遍历Vector。  

(3) 在AssocArray。java 中,修改这个例子,令其使用一个Hashtable,而不是AssocArray。  

(4) 获取练习 1 用到的 Gerbil 类,改为把它置入一个 Hashtable,然后将Gerbil 的名称作为一个String 

 (键)与置入表格的每个Gerbil (值)都关联起来。获得用于keys()的一个Enumeration,并用它在 

Hashtable 里遍历,查找每个键的 Gerbil,打印出键,然后将gerbil 告诉给 hop()。  

(5) 修改第 7 章的练习 1,用一个Vector 容纳Rodent (啮齿动物),并用Enumeration 在Rodent 序列中遍 

历。记住Vector 只能容纳对象,所以在访问单独的Rodent 时必须采用一个造型(如RTTI )。  

(6) 转到第 7 章的中间位置,找到那个GreenhouseControls。java (温室控制)例子,该例应该由三个文件 

构成。在Controller。java 中,类EventSet 仅是一个集合。修改它的代码,用一个 Stack 代替EventSet 。 

当然,这时可能并不仅仅用Stack 取代EventSet 这样简单;也需要用一个Enumeration 遍历事件集。可考虑 

在某些时候将集合当作 Stack 对待,另一些时候则当作 Vector 对待——这样或许能使事情变得更加简单。  

(7)  (有一定挑战性)在与所有Java 发行包配套提供的 Java 源码库中找出用于 Vector 的源码。复制这些代 

码,制作名为 intVector 的一个特殊版本,只在其中包含int 数据。思考是否能为所有基本数据类型都制作 

Vector 的一个特殊版本。接下来,考虑假如制作一个链接列表类,令其能随同所有基本数据类型使用,那么 

会发生什么情况。若在 Java 中提供了参数化类型,利用它们便可自动完成这一工作(还有其他许多好处)。  



                                                                        262 


…………………………………………………………Page 264……………………………………………………………

                      第 9 章  违例差错控制  



  

Java 的基本原理就是“形式错误的代码不会运行”。  

与C++类似,捕获错误最理想的是在编译期间,最好在试图运行程序以前。然而,并非所有错误都能在编译 
返回目录 上一页 下一页 回到顶部 10 9
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!