《list15》由会员上传分享,免费在线阅读,更多相关内容在学术论文-天天文库。
----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方第15章java.util第1部分:类集框架java.util包中包含了一些在Java2中新增加的最令人兴奋的增强功能:类集。一个类集(collection)是一组对象。类集的增加使得许多java.util中的成员在结构和体系结构上发生根本的改变。它也扩展了包可以被应用的任务范围。类集是被所有Java程序员紧密关注的最新型的技术。除了类集,java.util还包含了支持范围广泛的函数的各种各样的类和接口。这些类和接口被核心的Java包广泛使用,同时当然也可以被你编写的程序所使用。对它们的应用包括产生伪随机数,对日期和时间的操作,观测事件,对位集的操作以及标记字符串。由于java.util具有许多特性,因此它是Java中最被广泛使用的一个包。java.util中包含的类如下。在Java2中新增加的一些也被列出:AbstractCollection(Java2)EventObjectRandomAbstractList(Java2)GregorianCalendarResourceBundleAbstractMap(Java2)HashMap(Java2)SimpleTimeZoneAbstractSequentialList(Java2)HashSet(Java2)StackAbstractSet(Java2)HashtableStringTokenizerArrayList(Java2)LinkedList(Java2)Timer(Java2,v1.3)Arrays(Java2)ListResourceBundleTimerTask(Java2,v1.3)BitSetLocaleTimeZoneCalendarObservableTreeMap(Java2)Collections(Java2)PropertiesTreeSet(Java2)DatePropertyPermission(Java2)VectorDictionaryPropertyResourceBundleWeakHashMap(Java2)java.util定义了如下的接口。注意其中大多数是在Java2中新增加的。Collection(Java2)List(Java2)ObserverComparator(Java2)ListIterator(Java2)Set(Java2)EnumerationMap(Java2)SortedMap(Java2)EventListenerMap.Entry(Java2)SortedSet(Java2)Iterator(Java2)ResourceBundle类,ListResourceBundle类和PropertyResourceBundle类帮助具有特定地区资源的大型程序国际化。关于这些类的讨论,在这里从略。授权对系统属性进行读/写的PropertyPermission类也超过了本书的讨论范围。EventObject和EventListener类将在第20章讨论。下面将对剩下的类和接口做详细的讨论。
1----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方由于java.util包非常大,关于它的讨论将分成两章进行。本章讨论那些与对象的类集有关的成员。在第16章讨论其他的类和接口。15.1类集概述Java的类集(Collection)框架使你的程序处理对象组的方法标准化。在Java2出现之前,Java提供了一些专门的类如Dictionary,Vector,Stack和Properties去存储和操作对象组。尽管这些类非常有用,它们却缺少一个集中,统一的主题。因此例如说使用Vector的方法就会与使用Properties的方法不同。以前的专门的方法也没有被设计成易于扩展和能适应新的环境的形式。而类集解决了这些(以及其他的一些)问题。类集框架被设计用于适应几个目的。首先,这种框架是高性能的。对基本类集(动态数组,链接表,树和散列表)的实现是高效率的。一般很少需要人工去对这些“数据引擎”编写代码(如果有的话)。第二点,框架必须允许不同类型的类集以相同的方式和高度互操作方式工作。第三点,类集必须是容易扩展和/或修改的。为了实现这一目标,类集框架被设计成包含一组标准的接口。对这些接口,提供了几个标准的实现工具(例如LinkedList,HashSet和TreeSet),通常就是这样使用的。如果你愿意的话,也可以实现你自己的类集。为了方便起见,创建用于各种特殊目的的实现工具。一部分工具可以使你自己的类集实现更加容易。最后,增加了允许将标准数组融合到类集框架中的机制。算法(Algorithms)是类集机制的另一个重要部分。算法操作类集,它在Collections类中被定义为静态方法。因此它们可以被所有的类集所利用。每一个类集类不必实现它自己的方案,算法提供了一个处理类集的标准方法。由类集框架创建的另一项是Iterator接口。一个迭代程序(iterator)提供了一个多用途的,标准化的方法,用于每次访问类集的一个元素。因此迭代程序提供了一种枚举类集内容(enumeratingthecontentsofacollection)的方法。因为每一个类集都实现Iterator,所以通过由Iterator定义的方法,任一类集类的元素都能被访问到。因此,稍作修改,循环通过集合的程序代码也可以被用来循环通过列表。除了类集之外,框架定义了几个映射接口和类。映射(Maps)存储键/值对。尽管映射在对项的正确使用上不是“类集”,但它们完全用类集集成。在类集框架的语言中,可以获得映射的类集“视图”(collection-view)。这个“视图”包含了从存储在类集中的映射得到的元素。因此,如果选择了一个映射,就可以将其当做一个类集来处理。对于由java.util定义的原始类,类集机制被更新以便它们也能够集成到新的系统里。所以理解下面的说法是很重要的:尽管类集的增加改变了许多原始工具类的结构,但它却不会导致被抛弃。类集仅仅是提供了处理事情的一个更好的方法。最后的一点:如果你对C++比较熟悉的话,那么你可以发现Java的类集技术与在C++中定义的标准模板库(STL)相似。在C++中叫做容器(container),而在Java中叫做类集。
2----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方15.2类集接口类集框架定义了几个接口。本节对每一个接口都进行了概述。首先讨论类集接口是因为它们决定了collection类的基本特性。不同的是,具体类仅仅是提供了标准接口的不同实现。支持类集的接口总结在如下的表中:接口描述Collection能使你操作对象组,它位于类集层次结构的顶层List扩展Collection去处理序列(对象的列表)Set扩展Collection去处理集合,集合必须包含唯一元素SortedSet扩展Set去处理排序集合除了类集接口之外,类集也使用Comparator,Iterator和ListIterator接口。关于这些接口将在本章后面做更深入的描述。简单地说,Comparator接口定义了两个对象如何比较;Iterator和ListIterator接口枚举类集中的对象。为了在它们的使用中提供最大的灵活性,类集接口允许对一些方法进行选择。可选择的方法使得使用者可以更改类集的内容。支持这些方法的类集被称为可修改的(modifiable)。不允许修改其内容的类集被称为不可修改的(unmodifiable)。如果对一个不可修改的类集使用这些方法,将引发一个UnsupportedOperationException异常。所有内置的类集都是可修改的。下面讨论类集接口。15.2.1类集接口Collection接口是构造类集框架的基础。它声明所有类集都将拥有的核心方法。这些方法被总结在表15-1中。因为所有类集实现Collection,所以熟悉它的方法对于清楚地理解框架是必要的。其中几种方法可能会引发一个UnsupportedOperationException异常。正如上面解释的那样,这些发生在当类集不能被修改时。当一个对象与另一个对象不兼容,例如当企图增加一个不兼容的对象到一个类集中时。将产生一个ClassCastException异常。表15-1由Collection定义的方法方法描述booleanadd(Objectobj)将obj加入到调用类集中。如果obj被加入到类集中了,则返回true;如果obj已经是类集中的一个成员或类集不能被复制时,则返回falsebooleanaddAll(Collectionc)将c中的所有元素都加入到调用类集中,如果操作成功(也就是说元素被加入了),则返回true;否则返回falsevoidclear()从调用类集中删除所有元素
3----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方booleancontains(Objectobj)如果obj是调用类集的一个元素,则返回true,否则,返回false续表方法描述booleancontainsAll(Collectionc)如果调用类集包含了c中的所有元素,则返回true;否则,返回falsebooleanequals(Objectobj)如果调用类集与obj相等,则返回true;否则返回falseinthashCode()返回调用类集的散列码booleanisEmpty()如果调用类集是空的,则返回true;否则返回falseIteratoriterator()返回调用类集的迭代程序Booleanremove(Objectobj)从调用类集中删除obj的一个实例。如果这个元素被删除了,则返回true;否则返回falseBooleanremoveAll(Collectionc)从调用类集中删除c的所有元素。如果类集被改变了(也就是说元素被删除了),则返回true;否则返回falseBooleanretainAll(Collectionc)删除调用类集中除了包含在c中的元素之外的全部元素。如果类集被改变了(也就是说元素被删除了),则返回true,否则返回falseintsize()返回调用类集中元素的个数Object[]toArray()返回一个数组,该数组包含了所有存储在调用类集中的元素。数组元素是类集元素的拷贝Object[]toArray(Objectarray[])返回一个数组,该数组仅仅包含了那些类型与数组元素类型匹配的类集元素。数组元素是类集元素的拷贝。如果array的大小与匹配元素的个数相等,它们被返回到array。如果array的大小比匹配元素的个数小,将分配并返回一个所需大小的新数组,如果array的大小比匹配元素的个数大,在数组中,在类集元素之后的单元被置为null。如果任一类集元素的类型都不是array的子类型,则引发一个ArrayStoreException异常调用add( )方法可以将对象加入类集。注意add( )带一个Object类型的参数。因为Object是所有类的超类,所以任何类型的对象可以被存储在一个类集中。然而原始类型可能不行。例如,一个类集不能直接存储类型int,char,double等的值。当然如果想存储这些对象,也可以使用在第14章中介绍的原始类型包装器之一。可以通过调用addAll( )方法将一个类集的全部内容增加到另一个类集中。可以通过调用remove( )方法将一个对象删除。为了删除一组对象,可以调用removeAll( )方法。调用retainAll( )方法可以将除了一组指定的元素之外的所有元素删除。为了清空类集,可以调用clear( )方法。通过调用contains( )
4----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方方法,可以确定一个类集是否包含了一个指定的对象。为了确定一个类集是否包含了另一个类集的全部元素,可以调用containsAll( )方法。当一个类集是空的时候,可以通过调用isEmpty( )方法来予以确认。调用size( )方法可以获得类集中当前元素的个数。toArray( )方法返回一个数组,这个数组包含了存储在调用类集中的元素。这个方法比它初看上去的能力要更重要。经常使用类数组语法来处理类集的内容是有优势的。通过在类集和数组之间提供一条路径,可以充分利用这两者的优点。调用equals( )方法可以比较两个类集是否相等。“相等”的精确含义可以不同于从类集到类集。例如,可以执行equals( )方法以便用于比较存储在类集中的元素的值,换句话说,equals( )方法能比较对元素的引用。一个更加重要的方法是iterator( ),该方法对类集返回一个迭代程序。正如你将看到的那样,当使用一个类集框架时,迭代程序对于成功的编程来说是至关重要的。15.2.2List接口List接口扩展了Collection并声明存储一系列元素的类集的特性。使用一个基于零的下标,元素可以通过它们在列表中的位置被插入和访问。一个列表可以包含复制元素。除了由Collection定义的方法之外,List还定义了一些它自己的方法,这些方法总结在表15-2中。再次注意当类集不能被修改时,其中的几种方法引发UnsupportedOperationException异常。当一个对象与另一个不兼容,例如当企图将一个不兼容的对象加入一个类集中时,将产生ClassCastException异常。表15-2由List定义的方法方法描述voidadd(intindex,Objectobj)将obj插入调用列表,插入位置的下标由index传递。任何已存在的,在插入点以及插入点之后的元素将前移。因此,没有元素被覆盖booleanaddAll(intindex,Collectionc)将c中的所有元素插入到调用列表中,插入点的下标由index传递。在插入点以及插入点之后的元素将前移。因此,没有元素被覆盖。如果调用列表改变了,则返回true;否则返回falseObjectget(intindex)返回存储在调用类集内指定下标处的对象intindexOf(Objectobj)返回调用列表中obj的第一个实例的下标。如果obj不是列表中的元素,则返回-1intlastIndexOf(Objectobj)返回调用列表中obj的最后一个实例的下标。如果obj不是列表中的元素,则返回-1ListIteratorlistIterator()返回调用列表开始的迭代程序ListIteratorlistIterator(intindex)返回调用列表在指定下标处开始的迭代程序Objectremove(intindex)删除调用列表中index位置的元素并返回删除的元素。删除后,列表被压缩。也就是说,被删除元素后面的元素的下标减一
5----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方Objectset(intindex,Objectobj)用obj对调用列表内由index指定的位置进行赋值ListsubList(intstart,intend)返回一个列表,该列表包括了调用列表中从start到end–1的元素。返回列表中的元素也被调用对象引用对于由Collection定义的add( )和addAll( )方法,List增加了方法add(int,Object)和addAll(int,Collection)。这些方法在指定的下标处插入元素。由Collection定义的add(Object)和addAll(Collection)的语义也被List改变了,以便它们在列表的尾部增加元素。为了获得在指定位置存储的对象,可以用对象的下标调用get( )方法。为了给类表中的一个元素赋值,可以调用set( )方法,指定被改变的对象的下标。调用indexOf( )或lastIndexOf( )可以得到一个对象的下标。通过调用subList( )方法,可以获得列表的一个指定了开始下标和结束下标的子列表。正如你能想象到的,subList( )方法使得列表处理十分方便。15.2.3集合接口集合接口定义了一个集合。它扩展了Collection并说明了不允许复制元素的类集的特性。因此,如果试图将复制元素加到集合中时,add( )方法将返回false。它本身并没有定义任何附加的方法。15.2.4SortedSet接口SortedSet接口扩展了Set并说明了按升序排列的集合的特性。除了那些由Set定义的方法之外,由SortedSet接口说明的方法列在表15-3中。当没有项包含在调用集合中时,其中的几种方法引发NoSuchElementException异常。当对象与调用集合中的元素不兼容时,引发ClassCastException异常。如果试图使用null对象,而集合不允许null时,引发NullPointerException异常。表15-3由SortedSet定义的方法方法描述Comparatorcomparator()返回调用被排序集合的比较函数,如果对该集合使用自然顺序,则返回nullObjectfirst()返回调用被排序集合的第一个元素SortedSetheadSet(Objectend)返回一个包含那些小于end的元素的SortedSet,那些元素包含在调用被排序集合中。返回被排序集合中的元素也被调用被排序集合所引用Objectlast()返回调用被排序集合的最后一个元素SortedSetsubSet(Objectstart,Objectend)返回一个SortedSet,它包括了从start到end–1的元素。返回类集中的元素也被调用对象所引用SortedSettailSet(Objectstart)返回一个SortedSet
6----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方,它包含了那些包含在分类集合中的大于等于start的元素。返回集合中的元素也被调用对象所引用SortedSet定义了几种方法,使得对集合的处理更加方便。调用first( )方法,可以获得集合中的第一个对象。调用last( )方法,可以获得集合中的最后一个元素。调用subSet( )方法,可以获得排序集合的一个指定了第一个和最后一个对象的子集合。如果需要得到从集合的第一个元素开始的一个子集合,可以使用headSet( )方法。如果需要获得集合尾部的一个子集合,可以使用tailSet( )方法。
7----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方15.3Collection类现在,你已经熟悉了类集接口,下面开始讨论实现它们的标准类。一些类提供了完整的可以被使用的工具。另一些类是抽象的,提供主框架工具,作为创建具体类集的起始点。没有Collection类是同步的,但正如你将在本章后面看到的那样,有可能获得同步版本。标准的Collection类总结在下面的表中。类描述AbstractCollection实现大多数Collection接口AbstractList扩展AbstractCollection并实现大多数List接口AbstractSequentialList为了被类集使用而扩展AbstractList,该类集使用连续而不是随机方式访问其元素LinkedList通过扩展AbstractSequentialList来实现链接表ArrayList通过扩展AbstractList来实现动态数组AbstractSet扩展AbstractCollection并实现大多数Set接口HashSet为了使用散列表而扩展AbstractSetTreeSet实现存储在树中的一个集合。扩展AbstractSet注意:除了Collection类外,还有几个从以前版本遗留下来的类,如Vector,Stack和Hashtable均被重新设计成支持类集的形式。这些内容将在本章后面讨论。下面讨论具体的Collection类,举例说明它们的用法。15.3.1ArrayList类ArrayList类扩展AbstractList并执行List接口。ArrayList支持可随需要而增长的动态数组。在Java中,标准数组是定长的。在数组创建之后,它们不能被加长或缩短,这也就意味着你必须事先知道数组可以容纳多少元素。但是,你直到运行时才能知道需要多大的数组。为了解决这个问题,类集框架定义了ArrayList。本质上,ArrayList是对象引用的一个变长数组。也就是说,ArrayList能够动态地增加或减小其大小。数组列表以一个原始大小被创建。当超过了它的大小,类集自动增大。当对象被删除后,数组就可以缩小。注意:动态数组也被从以前版本遗留下来的类Vector所支持。关于这一点,将在本章后面介绍。ArrayList有如下的构造函数:ArrayList( )ArrayList(Collectionc)ArrayList(intcapacity)
8----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方其中第一个构造函数建立一个空的数组列表。第二个构造函数建立一个数组列表,该数组列表由类集c中的元素初始化。第三个构造函数建立一个数组列表,该数组有指定的初始容量(capacity)。容量是用于存储元素的基本数组的大小。当元素被追加到数组列表上时,容量会自动增加。下面的程序展示了ArrayList的一个简单应用。首先创建一个数组列表,接着添加类型String的对象(回想一个引用字符串被转化成一个字符串(String)对象)。接着列表被显示出来。将其中的一些元素删除后,再一次显示列表。//DemonstrateArrayList.importjava.util.*;classArrayListDemo{publicstaticvoidmain(Stringargs[]){//createanarraylistArrayListal=newArrayList();System.out.println("Initialsizeofal:"+al.size());//addelementstothearraylistal.add("C");al.add("A");al.add("E");al.add("B");al.add("D");al.add("F");al.add(1,"A2");System.out.println("Sizeofalafteradditions:"+al.size());//displaythearraylistSystem.out.println("Contentsofal:"+al);//Removeelementsfromthearraylistal.remove("F");al.remove(2);System.out.println("Sizeofalafterdeletions:"+al.size());System.out.println("Contentsofal:"+al);}}该程序的输出如下所示:Initialsizeofal:0Sizeofalafteradditions:7Contentsofal:[C,A2,A,E,B,D,F]Sizeofalafterdeletions:5Contentsofal:[C,A2,E,B,D]
9----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方注意a1开始时是空的,当添加元素后,它的大小增加了。当有元素被删除后,它的大小又会变小。在前面的例子中,使用由toString( )方法提供的默认的转换显示类集的内容,toString( )方法是从AbstractCollection继承下来的。尽管它对简短的例子程序来说是足够了,然而很少使用这种方法去显示实际中的类集的内容。通常编程者会提供自己的输出程序。但在下面的几个例子中,仍将采用由toString( )方法创建的默认输出。尽管当对象被存储在ArrayList对象中时,其容量会自动增加。仍可以通过调用ensureCapacity( )方法来人工地增加ArrayList的容量。如果事先知道将在当前能够容纳的类集中存储许许多多的项时,你可能会想这样做。在开始时,通过一次性地增加它的容量,就能避免后面的再分配。因为再分配是很花时间的,避免不必要的处理可以改善性能。ensureCapacity( )方法的特征如下所示:voidensureCapacity(intcap)这里,cap是新的容量。相反地,如果想要减小在ArrayList对象之下的数组的大小,以便它有正好容纳当前项的大小,可以调用trimToSize( )方法。该方法说明如下:voidtrimToSize( )从数组列表(ArrayList)获得数组(Array)当使用ArrayList时,有时想要获得一个实际的数组,这个数组包含了列表的内容。正如前面解释的那样,可以通过调用方法toArray( )来实现它。下面是几个为什么可能想将类集转换成为数组的原因:·对于特定的操作,可以获得更快的处理时间。·为了给方法传递数组,而方法不必重载去接收类集。·为了将新的基于类集的程序与不认识类集的老程序集成。无论何种原因,如下面的例子程序所示,将ArrayList转换成数组是一件繁琐的事情。//ConvertanArrayListintoanarray.importjava.util.*;classArrayListToArray{publicstaticvoidmain(Stringargs[]){//CreateanarraylistArrayListal=newArrayList();//Addelementstothearraylistal.add(newInteger(1));al.add(newInteger(2));al.add(newInteger(3));al.add(newInteger(4));
10----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方System.out.println("Contentsofal:"+al);//getarrayObjectia[]=al.toArray();intsum=0;//sumthearrayfor(inti=0;i 11----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方方法;为了删除最后一个元素,可以调用removeLast( )方法。它们的形式如下所示:ObjectremoveFirst( )ObjectremoveLast( )下面的程序举例说明了几个LinkedList支持的方法。//DemonstrateLinkedList.importjava.util.*;classLinkedListDemo{publicstaticvoidmain(Stringargs[]){//createalinkedlistLinkedListll=newLinkedList();//addelementstothelinkedlistll.add("F");ll.add("B");ll.add("D");ll.add("E");ll.add("C");ll.addLast("Z");ll.addFirst("A");ll.add(1,"A2");System.out.println("Originalcontentsofll:"+ll);//removeelementsfromthelinkedlistll.remove("F");ll.remove(2);System.out.println("Contentsofllafterdeletion:"+ll);//removefirstandlastelementsll.removeFirst();ll.removeLast();System.out.println("llafterdeletingfirstandlast:"+ll);//getandsetavalueObjectval=ll.get(2);ll.set(2,(String)val+"Changed");System.out.println("llafterchange:"+ll);}}该程序的输出如下所示:Originalcontentsofll:[A,A2,F,B,D,E,C, 12----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方Z]Contentsofllafterdeletion:[A,A2,D,E,C,Z]llafterdeletingfirstandlast:[A2,D,E,C]llafterchange:[A2,D,EChanged,C]因为LinkedList实现List接口,调用add(Object)将项目追加到列表的尾部,如同addLast( )方法所做的那样。使用add( )方法的add(int,Object)形式,插入项目到指定的位置,如例子程序中调用add(1,“A2”)的举例。注意如何通过调用get( )和set( )方法而使得ll中的第三个元素发生了改变。为了获得一个元素的当前值,通过get( )方法传递存储该元素的下标值。为了对这个下标位置赋一个新值,通过set( )方法传递下标和对应的新值。15.3.3HashSet类HashSet扩展AbstractSet并且实现Set接口。它创建一个类集,该类集使用散列表进行存储。正像大多数读者很可能知道的那样,散列表通过使用称之为散列法的机制来存储信息。在散列(hashing)中,一个关键字的信息内容被用来确定唯一的一个值,称为散列码(hashcode)。而散列码被用来当做与关键字相连的数据的存储下标。关键字到其散列码的转换是自动执行的——你看不到散列码本身。你的程序代码也不能直接索引散列表。散列法的优点在于即使对于大的集合,它允许一些基本操作如add( ),contains( ),remove( )和size( )方法的运行时间保持不变。下面的构造函数定义为:HashSet( )HashSet(Collectionc)HashSet(intcapacity)HashSet(intcapacity,floatfillRatio)第一种形式构造一个默认的散列集合。第二种形式用c中的元素初始化散列集合。第三种形式用capacity初始化散列集合的容量。第四种形式用它的参数初始化散列集合的容量和填充比(也称为加载容量)。填充比必须介于0.0与1.0之间,它决定在散列集合向上调整大小之前,有多少能被充满。具体的说,就是当元素的个数大于散列集合容量乘以它的填充比时,散列集合被扩大。对于没有获得填充比的构造函数,默认使用0.75。HashSet没有定义任何超过它的超类和接口提供的其他方法。重要的是,注意散列集合并没有确保其元素的顺序,因为散列法的处理通常不让自己参与创建排序集合。如果需要排序存储,另一种类集——TreeSet将是一个更好的选择。这里是一个说明HashSet的例子。//DemonstrateHashSet.importjava.util.*;classHashSetDemo{publicstaticvoidmain(Stringargs[]){//createahashsetHashSeths=newHashSet(); 13----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方//addelementstothehashseths.add("B");hs.add("A");hs.add("D");hs.add("E");hs.add("C");hs.add("F");System.out.println(hs);}}下面是该程序的输出:[A,F,E,D,C,B]如上面解释的那样,元素并没有按顺序进行存储。15.3.4TreeSet类TreeSet为使用树来进行存储的Set接口提供了一个工具,对象按升序存储。访问和检索是很快的。在存储了大量的需要进行快速检索的排序信息的情况下,TreeSet是一个很好的选择。下面的构造函数定义为:TreeSet( )TreeSet(Collectionc)TreeSet(Comparatorcomp)TreeSet(SortedSetss)第一种形式构造一个空的树集合,该树集合将根据其元素的自然顺序按升序排序。第二种形式构造一个包含了c的元素的树集合。第三种形式构造一个空的树集合,它按照由comp指定的比较函数进行排序(比较函数将在本章后面介绍)。第四种形式构造一个包含了ss的元素的树集合这里是一个说明TreeSet的例子。//DemonstrateTreeSet.importjava.util.*;classTreeSetDemo{publicstaticvoidmain(Stringargs[]){//CreateatreesetTreeSetts=newTreeSet();//Addelementstothetreesetts.add("C");ts.add("A");ts.add("B");ts.add("E");ts.add("F"); 14----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方ts.add("D");System.out.println(ts);}}这个程序的输出如下所示:[A,B,C,D,E,F]正如上面解释的那样,因为TreeSet按树存储其元素,它们被按照排序次序自动安排,如程序输出所示。15.4通过迭代函数访问类集通常希望循环通过类集中的元素。例如,可能会希望显示每一个元素。到目前为止,处理这个问题的最简单方法是使用iterator,iterator是一个或者实现Iterator或者实现ListIterator接口的对象。Iterator可以完成循环通过类集,从而获得或删除元素。ListIterator扩展Iterator,允许双向遍历列表,并可以修改单元。Iterator接口说明的方法总结在表15-4中。ListIterator接口说明的方法总结在表15-5中。表15-4由Iterator定义的方法方法描述booleanhasNext()如果存在更多的元素,则返回true,否则返回falseObjectnext()返回下一个元素。如果没有下一个元素,则引发NoSuchElementException异常voidremove()删除当前元素,如果试图在调用next()方法之后,调用remove()方法,则引发IllegalStateException异常表15-5由ListIterator定义的方法方法描述voidadd(Objectobj)将obj插入列表中的一个元素之前,该元素在下一次调用next()方法时,被返回booleanhasNext()如果存在下一个元素,则返回true;否则返回falsebooleanhasPrevious()如果存在前一个元素,则返回true;否则返回falseObjectnext()返回下一个元素,如果不存在下一个元素,则引发一个NoSuchElementException异常intnextIndex()返回下一个元素的下标,如果不存在下一个元素,则返回列表的大小Objectprevious()返回前一个元素,如果前一个元素不存在,则引发一个NoSuchElementException异常intpreviousIndex()返回前一个元素的下标,如果前一个元素不存在,则返回-1voidremove()从列表中删除当前元素。如果remove()方法在next()方法或previous() 15----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方方法调用之前被调用,则引发一个IllegalStateException异常voidset(Objectobj)将obj赋给当前元素。这是上一次调用next()方法或previous()方法最后返回的元素15.4.1使用迭代函数在通过迭代函数访问类集之前,必须得到一个迭代函数。每一个Collection类都提供一个iterator( )函数,该函数返回一个对类集头的迭代函数。通过使用这个迭代函数对象,可以访问类集中的每一个元素,一次一个元素。通常,使用迭代函数循环通过类集的内容,步骤如下:1.通过调用类集的iterator( )方法获得对类集头的迭代函数。2.建立一个调用hasNext( )方法的循环,只要hasNext( )返回true,就进行循环迭代。3.在循环内部,通过调用next( )方法来得到每一个元素。对于执行List的类集,也可以通过调用ListIterator来获得迭代函数。正如上面解释的那样,列表迭代函数提供了前向或后向访问类集的能力,并可让你修改元素。否则,ListIterator如同Iterator功能一样。这里是一个实现这些步骤的例子,说明了Iterator和ListIterator。它使用ArrayList对象,但是总的原则适用于任何类型的类集。当然,ListIterator只适用于那些实现List接口的类集。//Demonstrateiterators.importjava.util.*;classIteratorDemo{publicstaticvoidmain(Stringargs[]){//createanarraylistArrayListal=newArrayList();//addelementstothearraylistal.add("C");al.add("A");al.add("E");al.add("B");al.add("D");al.add("F");//useiteratortodisplaycontentsofalSystem.out.print("Originalcontentsofal:");Iteratoritr=al.iterator();while(itr.hasNext()){Objectelement=itr.next();System.out.print(element+"");}System.out.println();//modifyobjectsbeingiterated 16----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方ListIteratorlitr=al.listIterator();while(litr.hasNext()){Objectelement=litr.next();litr.set(element+"+");}System.out.print("Modifiedcontentsofal:");itr=al.iterator();while(itr.hasNext()){Objectelement=itr.next();System.out.print(element+"");}System.out.println();//now,displaythelistbackwardsSystem.out.print("Modifiedlistbackwards:");while(litr.hasPrevious()){Objectelement=litr.previous();System.out.print(element+"");}System.out.println();}}程序的输出如下所示:Originalcontentsofal:CAEBDFModifiedcontentsofal:C+A+E+B+D+F+Modifiedlistbackwards:F+D+B+E+A+C+特别值得注意的是:列表是如何被反向显示的。在列表被修改之后,litr指向列表的末端(记住,当到达列表末端时,litr.hasNext( )方法返回false)。为了以反向遍历列表,程序继续使用litr,但这一次,程序检测它是否有前一个元素。只要它有前一个元素,该元素就被获得并被显示出来。15.5将用户定义的类存储于Collection中为了简单,前面的例子在类集中存储内置的对象,如String或Integer。当然,类集并没有被限制为只能存储内置的对象。完全相反的是,类集的能力是它能存储任何类型的对象,包括你所创建的类的对象。例如,考虑下面的例子,在这个例子中使用LinkedList存储信箱地址。//Asimplemailinglistexample.importjava.util.*;classAddress{privateStringname;privateStringstreet;privateStringcity; 17----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方privateStringstate;privateStringcode;Address(Stringn,Strings,Stringc,Stringst,Stringcd){name=n;street=s;city=c;state=st;code=cd;}publicStringtoString(){returnname+" 18"+street+" 19"+city+""+state+""+code;}}classMailList{publicstaticvoidmain(Stringargs[]){LinkedListml=newLinkedList();//addelementstothelinkedlistml.add(newAddress("J.W.West","11OakAve","Urbana","IL","61801"));ml.add(newAddress("RalphBaker","1142MapleLane","Mahomet","IL","61853"));ml.add(newAddress("TomCarlton","867ElmSt","Champaign","IL","61820"));Iteratoritr=ml.iterator();while(itr.hasNext()){Objectelement=itr.next();System.out.println(element+" 20");}System.out.println();}}程序的输出如下所示:J.W.West11OakAveUrbanaIL61801RalphBaker1142MapleLaneMahometIL61853TomCarlton867ElmStChampaignIL61820 21----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方除了在类集中存储用户定义的类之外,关于上面程序的另一个重要的,值得注意的事情是它是非常短的。当考虑用50行代码建立一个能够实现存储,检索,以及处理信箱地址的链表时,类集框架的能力就变得显而易见了。正如大多数读者知道的那样,如果所有这些功能都必须用人工编写代码的话,程序将比现在的长好几倍。类集对许多不同的编程问题提供了现成的解决方案。每当情况出现时,就可以用它们。15.6处理映射正如在本章开始时所谈到的,除了类集,Java2还在java.util中增加了映射。映射(map)是一个存储关键字和值的关联或者说是关键字/值对的对象。给定一个关键字,可以得到它的值。关键字和值都是对象。关键字必须是唯一的。但值是可以被复制的。有些映射可以接收null关键字和null值。而有的则不行。15.6.1映射接口因为映射接口定义了映射的特征和本质,因此关于映射的讨论从这里开始。下面的接口支持映射:接口描述Map映射唯一关键字给值Map.Entry描述映射中的元素(关键字/值对)。这是Map的一个内部类SortedMap扩展Map以便关键字按升序保持下面对每个接口依次进行讨论。Map接口Map接口映射唯一关键字到值。关键字(key)是以后用于检索值的对象。给定一个关键字和一个值,可以存储这个值到一个Map对象中。当这个值被存储以后,就可以使用它的关键字来检索它。由Map说明的方法总结在表15-6中。当调用的映射中没有项存在时,其中的几种方法会引发一个NoSuchElementException异常。而当对象与映射中的元素不兼容时,引发一个ClassCastException异常。如果试图使用映射不允许使用的null对象时,则引发一个NullPointerException异常。当试图改变一个不允许修改的映射时,则引发一个UnsupportedOperationException异常。表15-6由Map定义的方法方法描述voidclear()从调用映射中删除所有的关键字/值对booleancontainsKey(Objectk)如果调用映射中包含了作为关键字的k,则返回true;否则返回false 22----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方booleancontainsValue(Objectv)如果映射中包含了作为值的v,则返回true;否则返回falseSetentrySet()返回包含了映射中的项的集合(Set)。该集合包含了类型Map.Entry的对象。这个方法为调用映射提供了一个集合“视图”Booleanequals(Objectobj)如果obj是一个Map并包含相同的输入,则返回true;否则返回falseObjectget(Objectk)返回与关键字k相关联的值inthashCode()返回调用映射的散列码booleanisEmpty()如果调用映射是空的,则返回true;否则返回falseSetkeySet()返回一个包含调用映射中关键字的集合(Set)。这个方法为调用映射的关键字提供了一个集合“视图”Objectput(Objectk,Objectv)将一个输入加入调用映射,覆盖原先与该关键字相关联的值。关键字和值分别为k和v。如果关键字已经不存在了,则返回null;否则,返回原先与关键字相关联的值voidputAll(Mapm)将所有来自m的输入加入调用映射Objectremove(Objectk)删除关键字等于k的输入续表方法描述intsize()返回映射中关键字/值对的个数Collectionvalues()返回一个包含了映射中的值的类集。这个方法为映射中的值提供了一个类集“视图”映射循环使用两个基本操作:get( )和put( )。使用put( )方法可以将一个指定了关键字和值的值加入映射。为了得到值,可以通过将关键字作为参数来调用get( )方法。调用返回该值。正如前面谈到的,映射不是类集,但可以获得映射的类集“视图”。为了实现这种功能,可以使用entrySet( )方法,它返回一个包含了映射中元素的集合(Set)。为了得到关键字的类集“视图”,可以使用keySet( )方法。为了得到值的类集“视图”,可以使用values( )方法。类集“视图”是将映射集成到类集框架内的手段。SortedMap接口SortedMap接口扩展了Map,它确保了各项按关键字升序排序。由SortedMap说明的方法总结在表15-7中。当调用映射中没有的项时,其中的几种方法引发一个NoSuchElementException异常。当对象与映射中的元素不兼容时,则引发一个ClassCastException异常。当试图使用映射不允许使用的null对象时,则引发一个NullPointerException异常。表15-7由SortedMap定义的方法方法描述Comparatorcomparator() 23----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方返回调用排序映射的比较函数。如果调用映射使用的是自然顺序的话,则返回nullObjectfirstKey()返回调用映射的第一个关键字SortedMapheadMap(Objectend)返回一个排序映射,该映射包含了那些关键字小于end的映射输入ObjectlastKey()返回调用映射的最后一个关键字SortedMapsubMap(Objectstart,Objectend)返回一个映射,该映射包含了那些关键字大于等于start同时小于end的输入SortedMaptailMap(Objectstart)返回一个映射,该映射包含了那些关键字大于等于start的输入排序映射允许对子映射(换句话说,就是映射的子集)进行高效的处理。使用headMap( ),tailMap( )或subMap( )方法可以获得子映射。调用firstKey( )方法可以获得集合的第一个关键字。而调用lastKey( )方法可以获得集合的最后一个关键字。Map.Entry接口Map.Entry接口使得可以操作映射的输入。回想由Map接口说明的entrySet( )方法,调用该方法返回一个包含映射输入的集合(Set)。这些集合元素的每一个都是一个Map.Entry对象。表15-8总结了由该接口说明的方法。表15-8由Map.Entry定义的方法方法描述booleanequals(Objectobj)如果obj是一个关键字和值都与调用对象相等的Map.Entry,则返回trueObjectgetKey()返回该映射项的关键字ObjectgetValue()返回该映射项的值inthashCode()返回该映射项的散列值ObjectsetValue(Objectv)将这个映射输入的值赋给v。如果v不是映射的正确类型,则引发一个ClassCastException异常。如果v存在问题,则引发一个IllegalArgumentException异常。如果v是null而映射又不允许null关键字,则引发一个NullPointerException异常。如果映射不能被改变,则引发一个UnsupportedOperationException异常。15.6.2映射类有几个类提供了映射接口的实现。可以被用做映射的类总结如下:类描述AbstractMap实现大多数的Map接口HashMap将AbstractMap扩展到使用散列表TreeMap将AbstractMap扩展到使用树 24----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方WeakHashMap将AbstractMap扩展到使用弱关键字散列表注意AbstractMap对三个具体的映射实现来说,是一个超类。WeakHashMap实现一个使用“弱关键字”的映射,它允许映射中的元素,当该映射的关键字不再被使用时,被放入回收站。关于这个类,在这里不做更深入的讨论。其他的类将在下面介绍。HashMap类HashMap类使用散列表实现Map接口。这允许一些基本操作如get( )和put( )的运行时间保持恒定,即便对大型集合,也是这样的。下面的构造函数定义为:HashMap( )HashMap(Mapm)HashMap(intcapacity)HashMap(intcapacity,floatfillRatio)第一种形式构造一个默认的散列映射。第二种形式用m的元素初始化散列映射。第三种形式将散列映射的容量初始化为capacity。第四种形式用它的参数同时初始化散列映射的容量和填充比。容量和填充比的含义与前面介绍的HashSet中的容量和填充比相同。HashMap实现Map并扩展AbstractMap。它本身并没有增加任何新的方法。应该注意的是散列映射并不保证它的元素的顺序。因此,元素加入散列映射的顺序并不一定是它们被迭代函数读出的顺序。下面的程序举例说明了HashMap。它将名字映射到账目资产平衡表。注意集合“视图”是如何获得和被使用的。importjava.util.*;classHashMapDemo{publicstaticvoidmain(Stringargs[]){//CreateahashmapHashMaphm=newHashMap();//Putelementstothemaphm.put("JohnDoe",newDouble(3434.34));hm.put("TomSmith",newDouble(123.22));hm.put("JaneBaker",newDouble(1378.00));hm.put("ToddHall",newDouble(99.22));hm.put("RalphSmith",newDouble(-19.08));//GetasetoftheentriesSetset=hm.entrySet();//GetaniteratorIteratori=set.iterator();//Displayelements 25----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方while(i.hasNext()){Map.Entryme=(Map.Entry)i.next();System.out.print(me.getKey()+":");System.out.println(me.getValue());}System.out.println();//Deposit1000intoJohnDoe'saccountdoublebalance=((Double)hm.get("JohnDoe")).doubleValue();hm.put("JohnDoe",newDouble(balance+1000));System.out.println("JohnDoe'snewbalance:"+hm.get("JohnDoe"));}}该程序的输出如下所示:ToddHall:99.22RalphSmith:-19.08JohnDoe:3434.34JaneBaker:1378.0TomSmith:123.22JohnDoe’scurrentbalance:4434.34程序开始创建一个散列映射,然后将名字的映射增加到平衡表中。接下来,映射的内容通过使用由调用函数entrySet( )而获得的集合“视图”而显示出来。关键字和值通过调用由Map.Entry定义的getKey( )和getValue( )方法而显示。注意存款是如何被制成JohnDoe的账目的。put( )方法自动用新值替换与指定关键字相关联的原先的值。因此,在JohnDoe的账目被更新后,散列映射将仍然仅仅保留一个“JohnDoe”账目。TreeMap类TreeMap类通过使用树实现Map接口。TreeMap提供了按排序顺序存储关键字/值对的有效手段,同时允许快速检索。应该注意的是,不像散列映射,树映射保证它的元素按照关键字升序排序。下面的TreeMap构造函数定义为:TreeMap( )TreeMap(Comparatorcomp)TreeMap(Mapm)TreeMap(SortedMapsm)第一种形式构造一个空树的映射,该映射使用其关键字的自然顺序来排序。第二种形式构造一个空的基于树的映射,该映射通过使用Comparatorcomp来排序(比较函数Comparators将在本章后面进行讨论)。第三种形式用从m的输入初始化树映射,该映射使用关键字的自然顺序来排序。第四种形式用从sm的输入来初始化一个树映射,该映射将按与sm相同的顺序来排序。 26----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方TreeMap实现SortedMap并且扩展AbstractMap。而它本身并没有另外定义其他方法。下面的程序重新使前面的例子运转,以便在其中使用TreeMap:importjava.util.*;classTreeMapDemo{publicstaticvoidmain(Stringargs[]){//CreateatreemapTreeMaptm=newTreeMap();//Putelementstothemaptm.put("JohnDoe",newDouble(3434.34));tm.put("TomSmith",newDouble(123.22));tm.put("JaneBaker",newDouble(1378.00));tm.put("ToddHall",newDouble(99.22));tm.put("RalphSmith",newDouble(-19.08));//GetasetoftheentriesSetset=tm.entrySet();//GetaniteratorIteratori=set.iterator();//Displayelementswhile(i.hasNext()){Map.Entryme=(Map.Entry)i.next();System.out.print(me.getKey()+":");System.out.println(me.getValue());}System.out.println();//Deposit1000intoJohnDoe'saccountdoublebalance=((Double)tm.get("JohnDoe")).doubleValue();tm.put("JohnDoe",newDouble(balance+1000));System.out.println("JohnDoe'snewbalance:"+tm.get("JohnDoe"));}}下面是该程序的输出结果:JaneBaker:1378.0JohnDoe:3434.34RalphSmith:-19.08ToddHall:99.22TomSmith:123.22JohnDoe’scurrentbalance:4434.34注意对关键字进行了排序。然而,在这种情况下,它们用名字而不是用姓进行了排序。可以通过在创建映射时,指定一个比较函数来改变这种排序。在下一节将介绍如何做。 27----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方15.7比较函数TreeSet和TreeMap都按排序顺序存储元素。然而,精确定义采用何种“排序顺序”的是比较函数。通常在默认的情况下,这些类通过使用被Java称之为“自然顺序”的顺序存储它们的元素,而这种顺序通常也是你所需要的(A在B的前面,1在2的前面,等等)。如果需要用不同的方法对元素进行排序,可以在构造集合或映射时,指定一个Comparator对象。这样做为你提供了一种精确控制如何将元素储存到排序类集和映射中的能力。Comparator接口定义了两个方法:compare( )和equals( )。这里给出的compare( )方法按顺序比较了两个元素:intcompare(Objectobj1,Objectobj2)obj1和obj2是被比较的两个对象。当两个对象相等时,该方法返回0;当obj1大于obj2时,返回一个正值;否则,返回一个负值。如果用于比较的对象的类型不兼容的话,该方法引发一个ClassCastException异常。通过覆盖compare( ),可以改变对象排序的方式。例如,通过创建一个颠倒比较输出的比较函数,可以实现按逆向排序。这里给出的equals( )方法,测试一个对象是否与调用比较函数相等:booleanequals(Objectobj)obj是被用来进行相等测试的对象。如果obj和调用对象都是Comparator的对象并且使用相同的排序。该方法返回true。否则返回false。重载equals( )方法是没有必要的,大多数简单的比较函数都不这样做。15.7.1使用比较函数下面是一个说明定制的比较函数能力的例子。该例子实现compare( )方法以便它按正常顺序的逆向进行操作。因此,它使得一个树集合按逆向的顺序进行存储。//Useacustomcomparator.importjava.util.*;//Areversecomparatorforstrings.classMyCompimplementsComparator{publicintcompare(Objecta,Objectb){StringaStr,bStr;aStr=(String)a;bStr=(String)b;//reversethecomparisonreturnbStr.compareTo(aStr);}//noneedtooverride 28----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方equals}classCompDemo{publicstaticvoidmain(Stringargs[]){//CreateatreesetTreeSetts=newTreeSet(newMyComp());//Addelementstothetreesetts.add("C");ts.add("A");ts.add("B");ts.add("E");ts.add("F");ts.add("D");//GetaniteratorIteratori=ts.iterator();//Displayelementswhile(i.hasNext()){Objectelement=i.next();System.out.print(element+"");}System.out.println();}}正如下面的输出所示,树按照逆向顺序进行存储:FEDCBA仔细观察实现Comparator并覆盖compare( )方法的MyComp类(正如前面所解释的那样,覆盖equals( )方法既不是必须的,也不是常用的)。在compare( )方法内部,String方法compareTo( )比较两个字符串。然而由bStr——不是aStr——调用compareTo( )方法,这导致比较的结果被逆向。对应一个更实际的例子,下面的例子是用TreeMap程序实现前面介绍的存储账目资产平衡表例子的程序。在前面介绍的程序中,账目是按名进行排序的,但程序是以按照名字进行排序开始的。下面的程序按姓对账目进行排序。为了实现这种功能,程序使用了比较函数来比较每一个账目下姓的排序。得到的映射是按姓进行排序的。//Useacomparatortosortaccountsbylastname.importjava.util.*;//Comparelastwholewordsintwostrings.classTCompimplementsComparator{publicintcompare(Objecta,Objectb){inti,j,k;StringaStr,bStr;aStr=(String)a;bStr=(String)b; 29----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方//findindexofbeginningoflastnamei=aStr.lastIndexOf('');j=bStr.lastIndexOf('');k=aStr.substring(i).compareTo(bStr.substring(j));if(k==0)//lastnamesmatch,checkentirenamereturnaStr.compareTo(bStr);elsereturnk;}//noneedtooverrideequals}classTreeMapDemo2{publicstaticvoidmain(Stringargs[]){//CreateatreemapTreeMaptm=newTreeMap(newTComp());//Putelementstothemaptm.put("JohnDoe",newDouble(3434.34));tm.put("TomSmith",newDouble(123.22));tm.put("JaneBaker",newDouble(1378.00));tm.put("ToddHall",newDouble(99.22));tm.put("RalphSmith",newDouble(-19.08));//GetasetoftheentriesSetset=tm.entrySet();//GetaniteratorIteratoritr=set.iterator();//Displayelementswhile(itr.hasNext()){Map.Entryme=(Map.Entry)itr.next();System.out.print(me.getKey()+":");System.out.println(me.getValue());}System.out.println();//Deposit1000intoJohnDoe'saccountdoublebalance=((Double)tm.get("JohnDoe")).doubleValue();tm.put("JohnDoe",newDouble(balance+1000));System.out.println("JohnDoe'snewbalance:"+tm.get("JohnDoe"));}}这里是程序的输出结果,注意此时的账目是按姓进行排序的:JaneBaker:1378.0JohnDoe:3434.34ToddHall:99.22RalphSmith: 30----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方-19.08TomSmith:123.22JohnDoe’snewbalance:4434.34比较函数类TComp比较两个包含姓和名的字符串。它首先比较姓。具体是这样做的,它首先寻找每一个字符串中最后一个空格的下标,然后比较从这个位置开始的每一个元素的子字符串。当两个字符串中姓完全相等时,它再比较两个名。这样就形成了一个先按姓进行排序,在姓相同的情况下,再按名字进行排序的树型映射。通过程序的输出中RalphSmith出现在TomSmith之前的结果可以看到这一点。15.8类集算法类集框架定义了几种能用于类集和映射的算法。在Collections类中,这些算法被定义为静态方法。表15-9中列出了这些算法。当试图比较不兼容的类型时,其中的一些算法引发一个ClassCastException异常;而当试图改变一个不可改变的类集时,则引发一个UnsupportedOperationException异常。表15-9由Collections定义的算法方法描述staticintbinarySearch(Listlist,Objectvalue,Comparatorc)按照c的次序在list中搜寻value。如果value在list内,则返回value在list的位置。如果在list中没有发现value,则返回-1staticintbinarySearch(Listlist,Objectvalue)在list中搜寻value,列表(list)必须被排序。如果value在list内,则返回value的位置。如果在list中没有发现value,则返回-1staticvoidcopy(Listlist1,Listlist2)将list2中的元素复制给list1续表方法描述staticEnumerationenumeration(Collectionc)返回c的一个枚举(参看本章后面的“枚举接口”)。staticvoidfill(Listlist,Objectobj)将obj赋给list中的每一个元素StaticObjectmax(Collectionc,Comparatorcomp)返回由comp确定的c中的最大元素staticObjectmax(Collectionc)返回按自然顺序确定的c中的最大元素。类集不必被排序staticObjectmin(Collectionc,Comparatorcomp)返回由comp确定的c中的最小元素。类集不必被排序staticObjectmin(Collectionc)返回按自然顺序确定的c中的最小元素staticListnCopies(intnum,Objectobj)返回包含在不可改变的列表中的obj的num个拷贝。num必须大于等于0staticvoidreverse(Listlist)将list中的序列逆向 31----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方staticComparatorreverseOrder()返回一个逆向比较函数(即将两个元素比较的结果进行逆向的比较函数)staticvoidshuffle(Listlist,Randomr)用r作为随机数的源,对list中的元素进行混淆(也即随机化)staticvoidshuffle(Listlist)对list中的元素进行混淆(也即随机化)staticSetsingleton(Objectobj)返回一个不可改变的集合obj。这是一个实现将单个对象变成集合的简单办法staticListsingletonList(Objectobj)返回一个不可改变的列表obj。这是一个实现将单个对象变成列表的简单办法(在Java2的1.3版中新增加的)staticMapsingletonMap(Objectk,Objectv)返回一个不可改变的关键字/值对映射k/v。这是一个实现将单个关键字/值对变成映射的简单办法(在Java2的1.3版中新增加的)staticvoidsort(Listlist,Comparatorcomp)按comp对list中的元素进行排序staticvoidsort(Listlist)按自然顺序对list中的元素进行排序staticCollectionsynchronizedCollection(Collectionc)返回一个被c支持的安全线程类集staticListsynchronizedList(Listlist)返回一个被list支持的安全线程列表staticMapsynchronizedMap(Mapm)返回一个被m支持的安全线程映射staticSetsynchronizedSet(Sets)返回一个被s支持的安全线程集合staticSortedMapsynchronizedSortedMap(SortedMapsm)返回一个被sm支持的安全线程排序集合staticSortedSetsynchronizedSortedSet(SortedSetss)返回一个被ss支持的安全线程集合staticCollectionunmodifiableCollection(Collectionc)返回一个被c支持的不可变类集StaticListunmodifiableList(Listlist)返回一个被list支持的不可变列表续表方法描述staticMapunmodifiableMap(Mapm)返回一个被m支持的不可变映射staticSetunmodifiableSet(Sets)返回一个被s支持的不可变集合StaticSortedMapunmodifiableSortedMap(SortedMapsm)返回一个被sm支持的不可变排序映射staticSortedSetunmodifiableSortedSet(SortedSetss)返回一个被ss支持的不可变排序集合注意其中的几种方法,如synchronizedList( )和synchronizedSet( )被用来获得各种类集的同步(安全线程)拷贝。正如前面解释的那样,没有任何一个标准类集实现是同步的。必须使用同步算法来为其提供同步。另一种观点:同步类集的迭代函数必须在synchronized 32----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方块内使用。以unmodifiable开头的一组方法返回不能被改变的各种类集“视图”。这些方法当将一些进程对类集设为只读形式时很有用的。Collections定义了三个静态变量:EMPTY_SET,EMPTY_LIST和EMPTY_MAP。它们都是不可改变的。EMPTY_MAP是在Java2的1.3版中新增加的。下面的程序说明了其中的一些算法。该程序创建和初始化了一个链表。reverseOrder( )方法返回一个对Integer对象的比较进行逆向的Comparator函数。列表中的元素按照这个比较函数进行排序并被显示出来。接下来,调用shuffle( )方法对列表进行随机排列。然后显示列表的最大和最小值。//Demonstratevariousalgorithms.importjava.util.*;classAlgorithmsDemo{publicstaticvoidmain(Stringargs[]){//CreateandinitializelinkedlistLinkedListll=newLinkedList();ll.add(newInteger(-8));ll.add(newInteger(20));ll.add(newInteger(-20));ll.add(newInteger(8));//CreateareverseordercomparatorComparatorr=Collections.reverseOrder();//SortlistbyusingthecomparatorCollections.sort(ll,r);//GetiteratorIteratorli=ll.iterator();System.out.print("Listsortedinreverse:");while(li.hasNext())System.out.print(li.next()+"");System.out.println();Collections.shuffle(ll);//displayrandomizedlistli=ll.iterator();System.out.print("Listshuffled:");while(li.hasNext())System.out.print(li.next()+"");System.out.println();System.out.println("Minimum:"+Collections.min(ll));System.out.println("Maximum:"+Collections.max(ll)); 33----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方}}该程序的输出如下所示:Listsortedinreverse:208-8-20Listshuffled:20-208-8Minimum:-20Maximum:20注意min( )和max( )方法是在列表被混淆之后,对其进行操作的。两者在运行时,都不需要排序的列表。15.9Arrays(数组)Java2在java.util中新增加了一个叫做Arrays的类。这个类提供了各种在进行数组运算时很有用的方法。尽管这些方法在技术上不属于类集框架,但它们提供了跨越类集和数组的桥梁。在这一节中,分析由Arrays定义的每一种方法。asList( )方法返回一个被指定数组支持的List。换句话说,列表和数组访问的是同一个单元。它具有如下的形式:staticListasList(Object[]array)这里array是包含了数据的数组。binarySearch( )方法使用二进制搜索寻找指定的值。该方法必须应用于排序数组。它具有如下的形式:staticintbinarySearch(byte[]array,bytevalue)staticintbinarySearch(char[]array,charvalue)staticintbinarySearch(double[]array,doublevalue)staticintbinarySearch(float[]array,floatvalue)staticintbinarySearch(int[]array,intvalue)staticintbinarySearch(long[]array,longvalue)staticintbinarySearch(short[]array,shortvalue)staticintbinarySearch(Object[]array,Objectvalue)staticintbinarySearch(Object[]array,Objectvalue,Comparatorc)这里,array是被搜索的数组,而value是被查找的值。当array中包含的元素是不可比较的(例如Double和StringBuffer)或者当value与array中的类型不兼容时,后两种形式引发一个ClassCastException异常。在最后一种形式中,比较函数(Comparator)c用于确定array中的元素的顺序。在所有的形式中,如果array中含有value,则返回该元素的下标。否则,返回一个负值。当两个数组相等时,equals( )方法返回true;否则返回false。equals( )方法具有下面的一些形式:staticbooleanequals(booleanarray1[],booleanarray2[])staticbooleanequals(bytearray1[],bytearray2[ 34----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方])staticbooleanequals(chararray1[],chararray2[])staticbooleanequals(doublearray1[],doublearray2[])staticbooleanequals(floatarray1[],floatarray2[])staticbooleanequals(intarray1[],intarray2[])staticbooleanequals(longarray1[],longarray2[])staticbooleanequals(shortarray1[],shortarray2[])staticbooleanequals(Objectarray1[],Objectarray2[])这里array1和array2是两个用来比较看是否相等的数组。fill( )方法将一个值赋给数组中的所有元素。换句话说,它用一个指定的值填充数组。fill( )方法有两种形式。第一种形式具有下面的一些形式,填充整个数组:staticvoidfill(booleanarray[],booleanvalue)staticvoidfill(bytearray[],bytevalue)staticvoidfill(chararray[],charvalue)staticvoidfill(doublearray[],doublevalue)staticvoidfill(floatarray[],floatvalue)staticvoidfill(intarray[],intvalue)staticvoidfill(longarray[],longvalue)staticvoidfill(shortarray[],shortvalue)staticvoidfill(Objectarray[],Objectvalue)这里value被赋给数组array中的每一个元素。fill( )方法的第二种形式将一个值赋给数组的一个子集。它的几种形式如下:staticvoidfill(booleanarray[],intstart,intend,booleanvalue)staticvoidfill(bytearray[],intstart,intend,bytevalue)staticvoidfill(chararray[],intstart,intend,charvalue)staticvoidfill(doublearray[],intstart,intend,doublevalue)staticvoidfill(floatarray[],intstart,intend,floatvalue)staticvoidfill(intarray[],intstart,intend,intvalue)staticvoidfill(longarray[],intstart,intend,longvalue)staticvoidfill(shortarray[],intstart,intend,shortvalue)staticvoidfill(Objectarray[],intstart,intend,Objectvalue)这里,value是赋给数组array中从start开始到end–1结束的子集的值。这些方法当start大于end时,都能引发一个IllegalArgumentException异常;而当start或end出界时,都能引发一个ArrayIndexOutOfBoundsException异常。sort( )方法对数组进行排序,以便数组能够按升序进行排列。sort( )方法有两种形式。下面给出的第一种形式对整个数组进行排序:staticvoidsort(bytearray[])staticvoidsort(chararray[])staticvoidsort(doublearray[])staticvoidsort(floatarray[])staticvoidsort(intarray[])staticvoidsort(longarray[])staticvoidsort(shortarray[])staticvoidsort(Objectarray[])staticvoidsort(Objectarray[],Comparatorc) 35----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方这里,array是被排序的数组。在最后的一种形式中,c是一个用来规定array中元素顺序的比较函数(Comparator)。当用于排序的数组中的元素不可比较时,这些对Object的数组进行排序的sort( )方法将引发一个ClassCastException异常。sort( )方法的第二种形式允许在一个数组内,指定一个想要进行排序的范围。它的具体形式如下:staticvoidsort(bytearray[],intstart,intend)staticvoidsort(chararray[],intstart,intend)staticvoidsort(doublearray[],intstart,intend)staticvoidsort(floatarray[],intstart,intend)staticvoidsort(intarray[],intstart,intend)staticvoidsort(longarray[],intstart,intend)staticvoidsort(shortarray[],intstart,intend)staticvoidsort(Objectarray[],intstart,intend)staticvoidsort(Objectarray[],intstart,intend,Comparatorc)这里,数组中想要进行排序的范围从start到end–1。在最后一种形式中,c是一个用来规定array中元素顺序的Comparator。如果start大于end,所有这些方法都能引发一个IllegalArgumentException异常;而当start或end出界时,又都能引发一个ArrayIndexOutOfBoundsException异常。当用于排序的数组中的元素不可比较时,最后两种形式也能引发一个ClassCastException异常。下面的程序举例说明了如何使用Arrays类中的一些方法://DemonstrateArraysimportjava.util.*;classArraysDemo{publicstaticvoidmain(Stringargs[]){//allocateandinitializearrayintarray[]=newint[10];for(inti=0;i<10;i++)array[i]=-3*i;//display,sort,displaySystem.out.print("Originalcontents:");display(array);Arrays.sort(array);System.out.print("Sorted:");display(array);//fillanddisplayArrays.fill(array,2,6,-1);System.out.print("Afterfill():");display(array);//sortanddisplayArrays.sort(array);System.out.print("Aftersortingagain:"); 36----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方display(array);//binarysearchfor-9System.out.print("Thevalue-9isatlocation");intindex=Arrays.binarySearch(array,-9);System.out.println(index);}staticvoiddisplay(intarray[]){for(inti=0;i 37----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方接口定义了可以对一个对象的类集中的元素进行枚举(一次获得一个)的方法。这个接口尽管没有被摈弃,但已经被Iterator所替代。Enumeration对新程序来说是过时的。然而它仍被几种从以前版本遗留下来的类(例如Vector和Properties)所定义的方法使用,被几种其他的API类所使用以及被目前广泛使用的应用程序所使用。Enumeration指定下面的两个方法:booleanhasMoreElements( )ObjectnextElement( )执行后,当仍有更多的元素可提取时,hasMoreElements( )方法一定返回true。当所有元素都被枚举了,则返回false。nextElement( )方法将枚举中的下一个对象做为一个类属Object的引用而返回。也就是每次调用nextElement( )方法获得枚举中的下一个对象。调用例程必须将那个对象置为包含在枚举内的对象类型。15.10.2VectorVector实现动态数组。这与ArrayList相似,但两者不同的是:Vector是同步的,并且它包含了许多不属于类集框架的从以前版本遗留下来的方法。随着Java2的公布,Vector被重新设计来扩展AbstractList和实现List接口,因此现在它与类集是完全兼容的。这里是Vector的构造函数:Vector( )Vector(intsize)Vector(intsize,intincr)Vector(Collectionc)第一种形式创建一个原始大小为10的默认矢量。第二种形式创建一个其原始容量由size指定的矢量。第三种形式创建一个其原始容量由size指定,并且它的增量由incr指定的矢量。增量指定了矢量每次允许向上改变大小的元素的个数。第四种形式创建一个包含了类集c中元素的矢量。这个构造函数是在Java2中新增加的。所有的矢量开始都有一个原始的容量。在这个原始容量达到以后,下一次再试图向矢量中存储对象时,矢量自动为那个对象分配空间同时为别的对象增加额外的空间。通过分配超过需要的内存,矢量减小了可能产生的分配的次数。这种次数的减少是很重要的,因为分配内存是很花时间的。在每次再分配中,分配的额外空间的总数由在创建矢量时指定的增量来确定。如果没有指定增量,在每个分配周期,矢量的大小增一倍。Vector定义了下面的保护数据成员:intcapacityIncrement;intelementCount;ObjectelementData[];增量值被存储在capacityIncrement中。矢量中的当前元素的个数被存储在elementCount中。保存矢量的数组被存储在elementData中。除了由List定义的类集方法之外,Vector还定义了几个从以前版本遗留下来的方法,这些方法列在表15-10中。 38----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方表15-10由Vector定义的方法方法描述finalvoidaddElement(Objectelement)将由element指定的对象加入矢量intcapacity()返回矢量的容量Objectclone()返回调用矢量的一个拷贝Booleancontains(Objectelement)如果element被包含在矢量中,则返回true;如果不包含于其中,则返回falsevoidcopyInto(Objectarray[])将包含在调用矢量中的元素复制到由array指定的数组中ObjectelementAt(intindex)返回由index指定位置的元素Enumerationelements()返回矢量中元素的一个枚举ObjectfirstElement()返回矢量的第一个元素intindexOf(Objectelement)返回element首次出现的位置下标。如果对象不在矢量中,则返回-1intindexOf(Objectelement,intstart)返回element在矢量中在start及其之后第一次出现的位置下标。如果该对象不属于矢量的这一部分,则返回-1voidinsertElementAt(Objectelement,intindex)在矢量中,在由index指定的位置处加入elementbooleanisEmpty()如果矢量是空的,则返回true。如果它包含了一个或更多个元素,则返回falseObjectlastElement()返回矢量中的最后一个元素intlastIndexOf(Objectelement)返回element在矢量中最后一次出现的位置下标。如果对象不包含在矢量中,则返回-1intlastIndexOf(Objectelement,intstart)返回element在矢量中,在start之前最后一次出现的位置下标。如果该对象不属于矢量的这一部分,则返回-1voidremoveAllElements()清空矢量,在这个方法执行以后,矢量的大小为0booleanremoveElement(Objectelement)从矢量中删除element。对于指定的对象,矢量中如果有其多个实例,则其中第一个实例被删除。如果成功删除,则返回true;如果没有发现对象,则返回falsevoidremoveElementAt(intindex)删除由index指定位置处的元素voidsetElementAt(Objectelement,intindex)将由index指定的位置分配给elementvoidsetSize(intsize)将矢量中元素的个数设为size。如果新的长度小于老的长度,元素将丢失;如果新的长度大于老的长度,则在其后增加null元素intsize()返回矢量中当前元素的个数续表方法描述StringtoString()返回矢量的字符串等价形式 39----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方voidtrimToSize()将矢量的容量设为与其当前拥有的元素的个数相等因为Vector实现List,所以可以像使用ArrayList的一个实例那样使用矢量。也可以使用它的从以前版本遗留下来的方法来操作它。例如,在后面实例化Vector,可以通过调用addElement( )方法而为其增加一个元素。调用elementAt( )方法可以获得指定位置处的元素。调用firstElement( )方法可以得到矢量的第一个元素。调用lastElement( )方法可以检索到矢量的最后一个元素。使用indexOf( )和lastIndexOf( )方法可以获得元素的下标。调用removeElement( )或removeElementAt( )方法可以删除元素。下面的程序使用矢量存储不同类型的数值对象。程序说明了几种由Vector定义的从以前版本遗留下来的方法,同时它也说明了枚举(Enumeration)接口。//DemonstratevariousVectoroperations.importjava.util.*;classVectorDemo{publicstaticvoidmain(Stringargs[]){//initialsizeis3,incrementis2Vectorv=newVector(3,2);System.out.println("Initialsize:"+v.size());System.out.println("Initialcapacity:"+v.capacity());v.addElement(newInteger(1));v.addElement(newInteger(2));v.addElement(newInteger(3));v.addElement(newInteger(4));System.out.println("Capacityafterfouradditions:"+v.capacity());v.addElement(newDouble(5.45));System.out.println("Currentcapacity:"+v.capacity());v.addElement(newDouble(6.08));v.addElement(newInteger(7));System.out.println("Currentcapacity:"+v.capacity());v.addElement(newFloat(9.4));v.addElement(newInteger(10));System.out.println("Currentcapacity:"+v.capacity());v.addElement(newInteger(11));v.addElement(newInteger(12));System.out.println("Firstelement:"+(Integer)v.firstElement()); 40----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方System.out.println("Lastelement:"+(Integer)v.lastElement());if(v.contains(newInteger(3)))System.out.println("Vectorcontains3.");//enumeratetheelementsinthevector.EnumerationvEnum=v.elements();System.out.println(" 41Elementsinvector:");while(vEnum.hasMoreElements())System.out.print(vEnum.nextElement()+"");System.out.println();}}该程序的输出如下所示:Initialsize:0Initialcapacity:3Capacityafterfouradditions:5Currentcapacity:5Currentcapacity:7Currentcapacity:9Firstelement:1Lastelement:12Vectorcontains3.Elementsinvector:12345.456.0879.4101112随着Java2的公布,Vector增加了对迭代函数的支持。现在可以使用迭代函数来替代枚举去遍历对象(正如前面的程序所做的那样)。例如,下面的基于迭代函数的程序代码可以被替换到上面的程序中://useaniteratortodisplaycontentsIteratorvItr=v.iterator();System.out.println(" 42Elementsinvector:");while(vItr.hasNext())System.out.print(vItr.next()+"");System.out.println();因为建议不要使编写枚举新的程序代码,所以通常可以使用迭代函数来对矢量的内容进行枚举。当然,业已存在的大量的老程序采用了枚举。不过幸运的是,枚举和迭代函数的工作方式几乎相同。15.10.3StackStack是Vector的一个子类,它实现标准的后进先出堆栈。Stack仅仅定义了创建空堆栈的默认构造函数。Stack包括了由Vector 43----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方定义的所有方法,同时增加了几种它自己定义的方法,具体总结在表15-11中。表15-11由Stack定义的方法方法描述booleanempty()如果堆栈是空的,则返回true,当堆栈包含有元素时,返回falseObjectpeek()返回位于栈顶的元素,但是并不在堆栈中删除它Objectpop()返回位于栈顶的元素,并在进程中删除它Objectpush(Objectelement)将element压入堆栈,同时也返回elementintsearch(Objectelement)在堆栈中搜索element,如果发现了,则返回它相对于栈顶的偏移量。否则,返回-1调用push( )方法可将一个对象压入栈顶。调用pop( )方法可以删除和返回栈顶的元素。当调用堆栈是空的时,如果调用pop( )方法,将引发一个EmptyStackException异常。调用peek( )方法返回但不删除栈顶的对象。调用empty( )方法,当堆栈中没有元素时,返回true。search( )方法确定一个对象是否存在于堆栈,并且返回将其指向栈顶所需的弹出次数。下面是一个创建堆栈的例子,在例子中,将几个整型(Integer)对象压入堆栈,然后再将它们弹出。//DemonstratetheStackclass.importjava.util.*;classStackDemo{staticvoidshowpush(Stackst,inta){st.push(newInteger(a));System.out.println("push("+a+")");System.out.println("stack:"+st);}staticvoidshowpop(Stackst){System.out.print("pop->");Integera=(Integer)st.pop();System.out.println(a);System.out.println("stack:"+st);}publicstaticvoidmain(Stringargs[]){Stackst=newStack();System.out.println("stack:"+st);showpush(st,42);showpush(st,66);showpush(st,99);showpop(st);showpop(st);showpop(st);try{showpop(st); 44----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方}catch(EmptyStackExceptione){System.out.println("emptystack");}}}下面是由该程序产生的输出。注意对于EmptyStackException的异常处理程序是如何被捕获以便于能够从容地处理堆栈的下溢:stack:[]push(42)stack:[42]push(66)stack:[42,66]push(99)stack:[42,66,99]pop->99stack:[42,66]pop->66stack:[42]pop->42stack:[]pop->emptystack15.10.4Dictionary字典(Dictionary)是一个表示关键字/值存储库的抽象类,同时它的操作也很像映射(Map)。给定一个关键字和值,可以将值存储到字典(Dictionary)对象中。一旦这个值被存储了,就能够用它的关键字来检索它。因此,与映射一样,字典可以被当做关键字/值对列表来考虑。尽管在Java2中并没有摈弃字典(Dictionary),由于它被映射(Map)所取代,从而被认为是过时的。然而由于目前Dictionary被广泛地使用,因此这里仍对它进行详细的讨论。由Dictionary定义的抽象方法在表15-12中列出。表15-12由Dictionary定义的抽象方法方法描述Enumerationelements()返回对包含在字典中的值的枚举Objectget(Objectkey)返回一个包含与key相连的值的对象。如果key不在字典中,则返回一个空对象booleanisEmpty()如果字典是空的,则返回true;如果字典中至少包含一个关键字,则返回falseEnumerationkeys()返回包含在字典中的关键字的枚举Objectput(Objectkey,Objectvalue)将一个关键字和它的值插入字典中。如果key已经不在字典中了,则返回null;如果key已经在字典中了,则返回与key相关联的前一个值 45----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方续表方法描述Objectremove(Objectkey)删除key和它的值。返回与key相关联的值。如果key不在字典中,则返回nullintsize()返回字典中的项数使用put( )方法在字典中增加关键字和值。使用get( )方法检索给定关键字的值。当分别使用keys( )和elements( )方法进行枚举(Enumeration)时,关键字和值可以分别逐个地返回。size( )方法返回存储在字典中的关键字/值对的个数。当字典是空的时候,isEmpty( )返回true。使用remove( )方法可以删除关键字/值对。注意:Dictionary类是过时的。应该执行Map接口去获得关键字/值存储的功能。15.10.5Hashtable散列表(Hashtable)是原始java.util中的一部分同时也是Dictionary的一个具体实现。然而,Java2重新设计了散列表(Hashtable)以便它也能实现映射(Map)接口。因此现在Hashtable也被集成到类集框架中。它与HashMap相似,但它是同步的。和HashMap一样,Hashtable将关键字/值对存储到散列表中。使用Hashtable时,指定一个对象作为关键字,同时指定与该关键字相关联的值。接着该关键字被散列,而把得到的散列值作为存储在表中的值的下标。散列表仅仅可以存储重载由Object定义的hashCode( )和equals( )方法的对象。hashCode( )方法计算和返回对象的散列码。当然,equals( )方法比较两个对象。幸运的是,许多Java内置的类已经实现了hashCode( )方法。例如,大多数常见的Hashtable类型使用字符串(String)对象作为关键字。String实现hashCode( )和equals( )方法。Hashtable的构造函数如下所示:Hashtable( )Hashtable(intsize)Hashtable(intsize,floatfillRatio)Hashtable(Mapm)第一种形式是默认的构造函数。第二种形式创建一个散列表,该散列表具有由size指定的原始大小。第三种形式创建一个散列表,该散列表具有由size指定的原始大小和由fillRatio指定的填充比。填充比必须介于0.0和1.0之间,它决定了在散列表向上调整大小之前散列表的充满度。具体地说,当元素的个数大于散列表的容量乘以它的填充比时,散列表被扩展。如果没有指定填充比,默认使用0.75。最后,第四种形式创建一个散列表,该散列表用m中的元素初始化。散列表的容量被设为m中元素的个数的两倍。默认的填充因子设为0.75。第四种构造函数是在Java2中新增加的。除了Hashtable目前实现的,由Map接口定义的方法之外,Hashtable定义的从以前版本遗留下来的方法列在表15-13中。 46----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方表15-13由Hashtable定义的从以前版本遗留下来的方法方法描述voidclear()复位并清空散列表Objectclone()返回调用对象的复制booleancontains(Objectvalue)如果一些值与存在于散列表中的value相等的话,则返回true;如果这个值不存在,则返回falsebooleancontainsKey(Objectkey)如果一些关键字与存在于散列表中的key相等的话,则返回true;如果这个关键字不存在,则返回falsebooleancontainsValue(Objectvalue)如果一些值与散列表中存在的value相等的话,返回true;如果这个值没有找到,则返回false(是一种为了保持一致性而在Java2中新增加的非Map方法)Enumerationelements()返回包含在散列表中的值的枚举Objectget(Objectkey)返回包含与key相关联的值的对象。如果key不在散列表中,则返回一个空对象booleanisEmpty()如果散列表是空的,则返回true;如果散列表中至少包含一个关键字,则返回falseEnumerationkeys()返回包含在散列表中的关键字的枚举Objectput(Objectkey,Objectvalue)将关键字和值插入散列表中。如果key已经不在散列表中,返回null。如果key已经存在于散列表中,则返回与key相连的前一个值voidrehash()增大散列表的大小并且对其关键字进行再散列。Objectremove(Objectkey)删除key及其对应的值。返回与key相关联的值。如果key不在散列表中,则返回一个空对象intsize()返回散列表中的项数StringtoString()返回散列表的等价字符串形式下面的例子重写前面介绍的关于银行账目的程序。在重写的程序中,使用Hashtable储存银行存款人的名字和他们当前的资产平衡表://DemonstrateaHashtableimportjava.util.*;classHTDemo{publicstaticvoidmain(Stringargs[]){Hashtablebalance=newHashtable();Enumerationnames;Stringstr;doublebal;balance.put("JohnDoe",newDouble(3434.34)); 47----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方balance.put("TomSmith",newDouble(123.22));balance.put("JaneBaker",newDouble(1378.00));balance.put("ToddHall",newDouble(99.22));balance.put("RalphSmith",newDouble(-19.08));//Showallbalancesinhashtable.names=balance.keys();while(names.hasMoreElements()){str=(String)names.nextElement();System.out.println(str+":"+balance.get(str));}System.out.println();//Deposit1,000intoJohnDoe'saccountbal=((Double)balance.get("JohnDoe")).doubleValue();balance.put("JohnDoe",newDouble(bal+1000));System.out.println("JohnDoe'snewbalance:"+balance.get("JohnDoe"));}}该程序的输出如下所示:ToddHall:99.22RalphSmith:-19.08JohnDoe:3434.34JaneBaker:1378.0TomSmith:123.22JohnDoe’snewbalance:4434.34重要的一点是:和映射类一样,Hashtable不直接支持迭代函数。因此,上面的程序使用枚举来显示balance的内容。然而,我们可以获得允许使用迭代函数的散列表的集合视图。为了实现它,可以简单地使用由Map定义的一个类集“视图”方法,如entrySet( )或keySet( )方法。例如,可以获得关键字的一个集合“视图”,并遍历这些关键字。下面是采用这种技术后重新编写的程序://UseiteratorswithaHashtable.importjava.util.*;classHTDemo2{publicstaticvoidmain(Stringargs[]){Hashtablebalance=newHashtable();Stringstr;doublebal;balance.put("JohnDoe",newDouble(3434.34));balance.put("TomSmith",newDouble(123.22));balance.put("JaneBaker",newDouble(1378.00));balance.put("ToddHall",newDouble(99.22)); 48----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方balance.put("RalphSmith",newDouble(-19.08));//showallbalancesinhashtableSetset=balance.keySet();//getset-viewofkeys//getiteratorIteratoritr=set.iterator();while(itr.hasNext()){str=(String)itr.next();System.out.println(str+":"+balance.get(str));}System.out.println();//Deposit1,000intoJohnDoe'saccountbal=((Double)balance.get("JohnDoe")).doubleValue();balance.put("JohnDoe",newDouble(bal+1000));System.out.println("JohnDoe'snewbalance:"+balance.get("JohnDoe"));}}15.10.6Properties属性(Properties)是Hashtable的一个子类。它用来保持值的列表,在其中关键字和值都是字符串(String)。Properties类被许多其他的Java类所使用。例如,当获得系统环境值时,System.getProperties( )返回对象的类型。Properties定义了下面的实例变量:Propertiesdefaults;这个变量包含了一个与属性(Properties)对象相关联的默认属性列表。Properties定义了如下的构造函数:Properties( )Properties(PropertiespropDefault)第一种形式创建一个没有默认值的属性(Properties)对象。第二种形式创建一个将propDefault作为其默认值的对象。在这两种情况下,属性列表都是空的。除了Properties从Hashtable中继承下来的方法之外,Properties自己定义的方法列在表15-14中。Properties也包含了一个不被赞成使用的方法:save( )。它被store( )方法所取代,因为它不能正确地处理错误。表15-14由Properties定义的从以前版本遗留下来的方法方法描述StringgetProperty(Stringkey)返回与key相关联的值。如果key 49----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方既不在列表中,也不在默认属性列表中,则返回一个null对象StringgetProperty(Stringkey,StringdefaultProperty)返回与key相关联的值。如果key既不在列表中,也不在默认属性列表中,则返回defaultPropertyvoidlist(PrintStreamstreamOut)将属性列表发送给与streamOut相链接的输出流voidlist(PrintWriterstreamOut)将属性列表发送给与streamOut相链接的输出流续表方法描述voidload(InputStreamstreamIn)throwsIOException从与streamIn相链接的输入数据流输入一个属性列表EnumerationpropertyNames()返回关键字的枚举,也包括那些在默认属性列表中找到的关键字ObjectsetProperty(Stringkey,Stringvalue)将value与key关联。返回与key关联的前一个值,如果不存在这样的关联,则返回null(为了保持一致性,在Java2中新增加的)voidstore(OutputStreamstreamOut,Stringdescription)在写入由description指定的字符串之后,属性列表被写入与streamOut相链接的输出流(在Java2中新增加的)Properties类的一个有用的功能是可以指定一个默认属性,如果没有值与特定的关键字相关联,则返回这个默认属性。例如,默认值可以与关键字一起在getProperty( )方法中被指定——如getProperty(“name”,“defaultvalue”)。如果“name”值没有找到,则返回“defaultvalue”。当构造一个Properties对象时,可以传递Properties的另一个实例做为新实例的默认值。在这种情况下,如果对一个给定的Properties对象调用getProperty(“foo”),而“foo”并不存在时,Java在默认Properties对象中寻找“foo”。它允许默认属性的任意层嵌套。下面的例子说明了Properties。该程序创建一个属性列表,在其中关键字是美国的州名,值是这些州的首府的名字。注意试图寻找包括默认值的Florida的首府时的情况。//DemonstrateaPropertylist.importjava.util.*;classPropDemo{publicstaticvoidmain(Stringargs[]){Propertiescapitals=newProperties();Setstates;Stringstr;capitals.put("Illinois","Springfield");capitals.put("Missouri","JeffersonCity");capitals.put("Washington","Olympia");capitals.put("California","Sacramento");capitals.put("Indiana","Indianapolis");//Showallstatesandcapitalsinhashtable. 50----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方states=capitals.keySet();//getset-viewofkeysIteratoritr=states.iterator();while(itr.hasNext()){str=(String)itr.next();System.out.println("Thecapitalof"+str+"is"+capitals.getProperty(str)+".");}System.out.println();//lookforstatenotinlist--specifydefaultstr=capitals.getProperty("Florida","NotFound");System.out.println("ThecapitalofFloridais"+str+".");}}该程序的输出如下所示:ThecapitalofMissouriisJeffersonCity.ThecapitalofIllinoisisSpringfield.ThecapitalofIndianaisIndianapolis.ThecapitalofCaliforniaisSacramento.ThecapitalofWashingtonisOlympia.ThecapitalofFloridaisNotFound.由于Florida不在列表中,所以使用了默认值。尽管当调用getProperty( )方法时,使用默认值是十分有效的,正如上面的程序所展示的那样,对大多数属性列表的应用来说,有更好的方法去处理默认值。为了更大的灵活性,当构造一个属性(Properties)对象时,指定一个默认的属性列表。如果在主列表中没有发现期望的关键字,将会搜索默认列表。例如,下面是对前面程序稍作修改的程序。在该程序中,有一个指定州的默认列表。在这种情况下,当搜索Florida时,将在默认列表中找到它。//Useadefaultpropertylist.importjava.util.*;classPropDemoDef{publicstaticvoidmain(Stringargs[]){PropertiesdefList=newProperties();defList.put("Florida","Tallahassee");defList.put("Wisconsin","Madison");Propertiescapitals=newProperties(defList);Setstates;Stringstr;capitals.put("Illinois","Springfield"); 51----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方capitals.put("Missouri","JeffersonCity");capitals.put("Washington","Olympia");capitals.put("California","Sacramento");capitals.put("Indiana","Indianapolis");//Showallstatesandcapitalsinhashtable.states=capitals.keySet();//getset-viewofkeysIteratoritr=states.iterator();while(itr.hasNext()){str=(String)itr.next();System.out.println("Thecapitalof"+str+"is"+capitals.getProperty(str)+".");}System.out.println();//Floridawillnowbefoundinthedefaultlist.str=capitals.getProperty("Florida");System.out.println("ThecapitalofFloridais"+str+".");}}15.10.7使用store( )和load( )Properties的一个最有用的方面是可以利用store( )和load( )方法方便地对包含在属性(Properties)对象中的信息进行存储或从盘中装入信息。在任何时候,都可以将一个属性(Properties)对象写入流或从中将其读出。这使得属性列表特别方便实现简单的数据库。例如,下面的程序使用属性列表创建一个简单的用计算机处理的存储着姓名和电话号码的电话本。为了寻找某人的电话号码,可输入他或者她的名字。程序使用store( )和load( )方法来存储和检索列表。当程序执行时,它首先试着从一个叫做phonebook.dat的文件中装入列表。如果这个文件存在,列表就被装入。然后就可以增加列表。如果这样做了,当终止程序时,新列表就会被保存。注意:实现一个小且实用的计算机化的电话号码本只需要很少的程序代码。/*Asimpletelephonenumberdatabasethatusesapropertylist.*/importjava.io.*;importjava.util.*;classPhonebook{publicstaticvoidmain(Stringargs[])throwsIOException{Propertiesht=newProperties();BufferedReaderbr=newBufferedReader(newInputStreamReader(System.in));Stringname,number;FileInputStreamfin=null; 52----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方booleanchanged=false;//Trytoopenphonebook.datfile.try{fin=newFileInputStream("phonebook.dat");}catch(FileNotFoundExceptione){//ignoremissingfile}/*Ifphonebookfilealreadyexists,loadexistingtelephonenumbers.*/try{if(fin!=null){ht.load(fin);fin.close();}}catch(IOExceptione){System.out.println("Errorreadingfile.");}//Letuserenternewnamesandnumbers.do{System.out.println("Enternewname"+"('quit'tostop):");name=br.readLine();if(name.equals("quit"))continue;System.out.println("Enternumber:");number=br.readLine();ht.put(name,number);changed=true;}while(!name.equals("quit"));//Ifphonebookdatahaschanged,saveit.if(changed){FileOutputStreamfout=newFileOutputStream("phonebook.dat");ht.store(fout,"TelephoneBook");fout.close();}//Lookupnumbersgivenaname.do{System.out.println("Enternametofind"+"('quit'toquit):");name=br.readLine();if(name.equals("quit"))continue;number=(String)ht.get(name);System.out.println(number);}while(!name.equals("quit")); 53----------专业最好文档,专业为你服务,急你所急,供你所需-------------文档下载最佳的地方}}15.11类集总结类集框架为程序员提供了一个功能强大的设计方案以解决编程过程中面临的大多数任务。下一次当你需要存储和检索信息时,可考虑使用类集。记住,类集不仅仅是专为那些“大型作业”,例如联合数据库,邮件列表或产品清单系统等所专用的。它们对于一些小型作业也是很有效的。例如,TreeMap可以给出一个很好的类集以保留一组文件的字典结构。TreeSet在存储工程管理信息时是十分有用的。坦白地说,对于采用基于类集的解决方案而受益的问题种类只受限于你的想象力。
此文档下载收益归作者所有