Java数组与容器类分析资料–数组、List和Set、Map等

来源:互联网 发布:查商品价格的软件 编辑:程序博客网 时间:2024/06/05 16:56

Java言内置的型,除此之外,Java有多保存象引用的方式。Java类库提供了一套相当完整的容器,使用的方法可以保存和操纵对象。下面分别进讨论,在研究Java容器之前,先了解一下Java的基本功能和特性。

1.  的基本特性

与其它种类的容器(List/Set/Map)的区在于效率、确定的型和保存基本型数据的能力。数是一高效的存和随机访问对象引用序列的方式,使用数可以快速的访问中的元素。但是当建一个数组对(注意和象数的区)后,数的大小也就固定了,当数不足的候就再建一个新的数,把旧的数中所有的引用制到新的数中。

Java中的数和容器都需要检查,如果越界就会得到一个RuntimeException异常。点和C++中有所不同,C++vector的操作符[]不会做检查在速度上会有一定的提高,Java的数和容器会因为时刻存在的检查带来一些性能上的开销

Java中通用的容器不会以具体的型来象,容器中的象都是以Object理的,Java中所有的基。另外,数可以保存基本型,而容器不能,它只能保存任意的Java象。

一般情况下,考到效率与检查应该尽可能考使用数。如果要解决一般化的问题,数可能会受到一些限制,这时可以使用Java提供的容器

2.  操作数用功能

java.util.Arrays中,有static方法,提供了操作数的一些基本功能:

equals()方法—-用于比两个数是否相等,相等的条件是两个数的元素个数必相等,并且对应位置的元素也相等。

fill()方法—-用以某个填充整个数个方法有点笨。

asList()方法—-接受任意的数组为参数,将其转变为List容器。

binarySearch()方法—-用于在已排序的数找元素,需要注意的是必是已排序的数。当Arrays.binarySearch()找到了找目标时方法将返回一个等于或大于0,否将返回一个负值,表示在目前的排序状下此目元素所应该插入的位置。负值算公式是“-x-1”x指的是第一个大于象的元素在数中的位置,如果数中所有的元素都小于要找的象,x = a.size()。如果数中包含重的元素,无法保找到的是哪一个元素,如果需要没有重元素的数排序,可以使用TreeSet或者LinkedHashSet。另外,如果使用Comparator排序了某个象数,在使用方法提供同Comparator型的参数。需要注意的是,基本型数无法使用Comparator行排序。

sort()方法—-组进行升序排序。

Java类库中,另有static方法System.arraycopy()用来制数,它针对所有型做了重

3.  的排序

Java1.01.1两个版本中,类库缺少基本的算法操作,包括排序的操作,Java2行了改善。在行排序的操作,需要根据象的实际类行比操作,如果为每种不同的型各自写一个不同的排序方法,将会使得代用。一般的程序设计标应将保持不的事物与会的事物相分离。在里,不的是通用的排序算法,化的是各种对象相互比的方式。

Java有两方式来实现的功能,一实现java.lang.Comparable接口,接口只有一个compareTo()方法,并以一个Object类为参数,如果当前象小于参数返回负值,如果相等返回零,如果当前象大于参数返回正。另一方法是采用策略(strategy)设计模式,将会化的代封装在它自己的(策略)中,再将策略象交保持不的代中,后者使用此策略实现它的算法。因此,可以不同的比方式生成不同的象,将它用在同的排序程序中。在此情况下,通一个实现Comparator接口的建了一个策略,个策略compare()equals()两个方法,一般情况下实现compare()方法即可。

使用上述两方法即可任意基本型的数组进行排序,也可以任意的象数组进行排序。再提示一遍,基本无法使用Comparator行排序。

Java类库中的排序算法针对排序的行了——针对基本设计快速排序针对对设计并排序。一般不用担心其性能。

Java容器分析–ListSet

容器可以大大提高程效率和程能力,在Java2中,所有的容器都由SUN公司的Joshua Bloch行了重新设计,丰富了容器类库的功能。

Java2容器类类库的用途是保存,它分

Collection—-独立的元素,通常些元素都服从某种规则List保持元素特定的序,而Set不能有重元素。

Map—-键值对象,即其元素是成象,最典型的用就是数据字典,并且有其它广泛的用。另外,Map可以返回其所有键组成的Set和其所有值组成的Collection,或其键值对组成的Set,并且可以像数样扩展多Map,只要Map键值对是一个Map即可。

1.迭代器

迭代器是一种设计模式,它是一个象,它可以遍选择序列中的象,而开发不需要了解序列的底层结构。迭代器通常被称象,因为创建它的代价小。

Java中的Iterator功能比较简单,并且只能向移

(1)    使用方法iterator()要求容器返回一个Iterator。第一次Iteratornext()方法,它返回序列的第一个元素。

(2)    使用next()得序列中的下一个元素。

(3)    使用hasNext()检查序列中是否有元素。

(4)    使用remove()将迭代器新返回的元素除。

IteratorJava迭代器最简单实现List设计ListIterator具有更多的功能,它可以从两个方向遍List,也可以从List中插入和除元素。

2.List的功能方法

List(interface): 次序是List最重要的特点;它确保维护元素特定的序。ListCollection添加了多方法,使得能List插入与移除元素(只推荐LinkedList使用)。一个List可以生成ListIterator,使用它可以从两个方向遍List,也可以从List插入和除元素。

ArrayList: 由数组实现List。它允许对元素行快速随机访问,但是向List插入与移除元素的速度很慢。ListIterator应该用来由后向前遍ArrayList,而不是用来插入和除元素,因为这LinkedList开销要大很多。

LinkedList: 对顺访问进行了化,向List插入与除得开销不大,随机访问则对较(可用ArrayList代替)。它具有方法addFirst()addLast()getFirst()getLast()removeFirst()removeLast()些方法(没有在任何接口或基中定义过)使得LinkedList可以当作堆列和双向列使用。

3.Set的功能方法

Set(interface): 存入Set个元素必是唯一的,因Set不保存重元素。加入SetObjectequals()方法以确保象的唯一性。SetCollection有完全一的接口。Set接口不保证维护元素的次序。

HashSet: 快速找而设计Set。存入HashSet象必hashCode()

TreeSet: 保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。

LinkedHashSet: 具有HashSet查询速度,且内部使用维护元素的(插入的次序)。于是在使用迭代器遍Set果会按元素插入的次序示。

HashSet采用散列函数元素行排序,专门为快速查询设计的;TreeSet采用的数据行排序元素;LinkedHashSet内部使用散列以加快查询速度,同使用维护元素的次序,使得看起来元素是以插入的序保存的。需要注意的是,生成自己的类时Set需要维护元素的存储顺序,因此要实现Comparable接口并定compareTo()方法。

Java容器分析–Map

准的Java类库中包含了几种类型的Map,它有同的基本接口Map,但是行特性各不相同,主要表在效率、键值对的保存、元素呈次序、象的保存周期和判定是否等价的策略等方面。

1.Map的功能方法

Map(interface): 维护labelvalue关联性,使得可以通labelvalue

HashMap: Map基于散列表的实现,取代了Hashtable。插入和查询label/value开销是固定的,并且可以通构造器置容量和负载子,以整容器的性能。

LinkedHashMap: HashMap的基上做了一些改,在迭代遍,取得label/value序是其插入的次序,或者是最近最少使用(LRU)的次序,速度上比HashMap要慢一点,但在迭代访问时速度会更快,主要原因是它使用了维护内部次序。

TreeMap: labellabel/value,元素会被排序,其次序由ComparableComparator决定,因此查询所得到的果是经过排序的。另外,它是唯一subMap()方法的Map具体,即返回一个子。它也是SortedMap接口的唯一实现subMap()方法也是从接口承的。

WeakHashMap: Weak Key映射,允许释放映射所指向的象。当映射之外没有引用指向某个label,此label可以被垃圾收集器回收。

IdentityHashMap: 使用==代替equals()label行比的散列映射。

2.hashCode()

当使用中的IntegerHashMaplabel,程序能正常运行,但是使用自己建的HashMaplabel,通常犯一个错误

HashMap中通labelvalue实际上是label象地址的散列来确定value的。一般情况下,我是使用基Object的方法hashCode()来生成散列,它默是使用象的地址来算的,因此由第一个new Apple(5)和第二个new Apple(5)生成的散列是不同的,不能完成正确的找。通常,我可以写自己的hashCode()方法来覆盖基的原始方法,但与此同,我时实现equals()方法来判断当前的label是否与表中存在的label相同。正确的equals()方法足五个条件:

(1)     自反性。于任意的xx.equals(x)一定返回true

(2)     称性。于任意的xy,如果y.equals(x)返回truex.equals(y)也返回true

(3)     传递性。于任意的xyz,如果有x.equals(y)返回truey.equals(z)返回truex.equals(z)一定返回true

(4)     一致性。于任意的xy,如果象中用于等价比的信息没有改,那论调x.equals(y)多少次,返回的应该保持一致,要一直是true,要一直是false

(5)     任何不是nullxx.equals(null)一定返回false

equals()的是象的地址,如果要使用自己的HashMaplabel,必hashCode()equals()方法。

使用散列的目的:想要使用一个象来找另一个象。使用TreeSetTreeMap也能实现此目的。另外,可以自己实现一个Map,此,必提供Map.entrySet()方法来生成Map.Entry象的Set

使用散列的价:速度,散列使得查询可以快速行。散列将label保存中方便快速查询,因元素最快的数据构是数,用它来表示label的信息(后面有信息的描述),而不是label本身。通label算得到一个数字,作的下个数字就是散列(即前面所述的信息)散列具体是通在基Object中,可能由程序自定覆盖的hashCode()方法,即散列函数生成。了解决数容量来的限制,可以使不同的label生成相同的下,保存在一个list中,一个表就是数的一个元素。查询label就可以通过对list中的信息找,当散列函数比好,数个位置中的list短,可以快速找到数元素list中的某个位置,提高了整体速度。

散列表中的slot通常称bucket了使散列分均匀,bucket一般取数。但事实证明,实际上并不是散列bucket的理想容量,近来Java散列实现都使用2,具体如何验证以后再

3.HashMap的性能因子

容量(capacity):散列表中bucket的数量。

初始化容量(initial capacity):建散列表bucket的数量。可以在构造方法中指定HashMapHashSet的初始化容量。

尺寸(size):散列表中记录的数量。(的元素个数,非list中元素)

负载因子(load factor):尺寸/容量。负载因子0,表示空的散列表,0.5表示半的散列表。轻负载的散列表具有冲突少,适宜插入与查询的特点,但是使用迭代器遍会比慢。高的负载会减少所需空大小。当负载达到指定值时,容器会自成倍地增加容量,并将原有的象重新分配,存入新的bucket中,程称重散列

4.重写hashCode()关键

(1)     同一个hashCode()应该生成同

(2)     hashCode()方法不要依象中易的数据,当数据hashCode()就会生成一个不同的散列,即生了一个不同的label

(3)     hashCode()于具有唯一性的象信息,例如象地址。

(4)     散列码应该心速度,而不是唯一性,因散列不必是唯一的。

(5)     好的hashCode()应该产生分均匀的散列。在Effective Java(Addison-Wesley 2001)中,Joshua BlochhashCode()出了设计,可以参考。

写正确高效的hashCode()equals()可以参考ApacheJakarta Commons目中的工具。

java集合类总结

象的集合

如果程序的象数量有限,且寿命可知,那么这个程序是相当简单的。

与其它容器的区在三个方面:效率,识别以及可以持有primitives。数Java提供的,能随机存访问reference序列的多方法中的,最高效的一。数是一个简单线性序列,所有它可以快速的访问其中的元素。但是速度是有代价的;当你建了一个数之后,它的容量就固定了,而且在其生命周期里不能改。也你会提建一个数,等到快不用的候,再建一个新的,然后将旧的数里的reference全部到新的里面。其(我以后会的)ArrayList就是这么做的。但是这种灵活性所来的开销,使得ArrayList的效率比起数有了明下降。

Java和容器都做检查;如果了界,它旧会一个RuntimeException这种异常表明错误是由程序造成的,这样你就用不着再在程序里面检查了。

有一些泛型容器包括ListSetMap。他们处象的候就好像象都没有自己的具体型一。也就是,容器将它所含的元素都看成是(Java中所有的根Object的。这样你只需要建一容器,就能把所有型的象全都放去。从个角度来看,这种做法很不(只是苦了 primitive。如果是常量,你可以用JavaprimitiveWrapper;如果是量,那就只能放在你自己的里了)。与其他泛型容器相比,里体的第二革优势建数候,你也同指明了它所持有的象的型(又引出了第三点--数可以持有primitives,而容器却不行)。也就是,它会在编译候作检查,从而防止你插入错误类型的象,或者是在提取象的候把象的了。Java编译和运行都能阻止你将一个不恰当的消息传给对象。所有并不是使用容器就有什,只是如果编译器能帮你指定,那程序运行会更快,最也会少收到程序运行异常骚扰

从效率和检查的角度来看,使用数组总是没的。但是,如果你在解决一个更一般的问题,那数就会得功能太弱了点。

是第一流的

不管你用的是那种类型的数,数标识实际上都是一个建在堆(heap)里的实实在在的象的”reference实际上是那个象持有其他象的reference。你即可以用数的初始化句,含地象,也可以用new表达式,明确地象,只length属性能告你数能存多少元素。它是数组对象的一部分(实际上也是你唯一能访问的属性或方法)‘[]’法是另一条访问组对象的途径。

你没法知道数里面究竟放了多少元素,因length只是告你数能放多少元素,也就是是数组对象的容量,而不是它真正已持有的元素的数量。但是,建数组对象的候,它所持有的reference都会被自地初始化null,所以你可以通过检查的某个槽位是否null,来判断它是否持有象。以此推,primitive的数,会自来数字初始化零,字符初始化(char)0boolean初始化false

primitive容器

容器只能持有Object象的reference。而数除了能持有Objectsreference之外,可以直接持有primitive。当然可以使用IntegerDoublewrapper。把primitive放到容器中,淡这样总有点怪怪的。此外, primitive的效率要比wrapper容器的高出多。

当然,如果你使用primitive候,需要那能随需要自动扩展的容器的灵活性,那就不能用数了。你只能用容器来存primitivewrapper

返回一个数

你写了一个方法,它返回的不是一个而是一组东西。那Java中就可以返回的就是一个数。与C++不同,你永也不必Java的数操心--只要你需要它,它就在;一旦你用完了,垃圾回收器会帮你把它打

Arrays

java.util里面有一个Arrays,它包括了一可用于数static方法,些方法都是一些用工具。其中有四个基本方法:用来比两个数是否相等的equals();用来填充的fill();用来组进行排序的sort();以及用于在一个已排序的数找元素的 binarySearch()。所有些方法都primitiveObject行了重。此外有一个asList()方法,它接受一个数,然后把它成一个List容器。

Arrays是有用的,但它的功能并不完整。例来,如果它能不用写for就能直接打印数,那就好了。此外,正如你所看到的fill()只能用一个填数。所以,如果你想把随即生成的数字填fill()是无能力的。

制一个数

Java类库提供了一个System.arraycopy()static方法。相比for,它能以更快的速度拷System.arraycopy()所有型都作了重

象数primitive都能拷。但是如果你拷的是象数,那你只拷了它reference--象本身不会被拷被成浅拷shallow copy)。

的比

了能比是否完全相等,Arrays提供了equals()方法。当然,也是针对primitive以及Object的。两个数要想完全相等,他有相同数量的元素,而且数个元素必与另一个数的相对应的位置上的元素相等。元素的相等姓,用equals()判断。( primitive,它会使用其wrapperequals();比如int使用Integer.equals()。)。

元素的比

Java里面有两实现功能的方法。一是实现java.lang.Comparable接口,并以此实现类自有的方法。是一个很简单的接口,它只有一个方法compareTo()个方法能接受另一个象作参数,如果象比参数小,它就会返回一个数,如果相同返回零,如果有的象比参数大,它就返回一个正数。

static randInt()方法会生成一个介于0100的正数。

在架,有人你一个没有实现Comparable接口的,或者类实现Comparable接口,但是你发现它的工作方式不是你所希望的,于是要重新定一个新的比方法。Java没有求你一定要把进类里,它的解决方案是使用策略模式(strategy design pattern。有了策略之后,你就能把会的代封装到它自己的里(即所的策略strategy object)。你把策略象交不会的代,然后用它运用策略完成整个算法。这样,你就可以用不同的策略象来表示不同的比方法,然后把它都交同一个排序程序了。接下来就要过实现Comparator接口来定策略象了。个接口有两个方法compare()equals()。但是除非是有特殊的性能要求,否你用不着去实现equals()。因只要是,它就都含地承自Object,而Object里面已有了一个 equals()了。所以你尽可以使用缺省的Objectequals()这样就已经满足接口的要求了。

Collections专门有一个会返回与象自有的比法相反的Comparator的方法。它能很易地被用到CompType上面。

Collections.reverseOrder()返回了一个Comparatorreference

compare()方法会根据第一个参数是小于,等于是大于第二个参数,分返回整数,零或是正整数。

的排序

有了内置的排序方法之后,你就能任何数排序了,不primitive象数的,只要它实现Comparable接口或有一个与之相Comparator象就行了。

Java类库所用的排序算法已作了化--primitive,它用的是快速排序(Quicksort对对象,它用的是定合并排序(stable merge sort。所以除非是prolier表明排序算法是瓶,否你不用性能担心。

查询有序数

一旦数排完序,你就能Arrays.binarySearch()行快速查询了。但是切忌一个尚未排序的数使用binarySearch();因为这么做的果是没意的。

如果Arrays.binarySearch()找到了,它就返回一个大于或等于0。否它就返回一个负值,而负值要表达的意思是,如果你手动维护这个数值应该插在哪个止。就是:

-(插入点)-1

插入点就是,在所有比要找的那个更大中,最小的那个的下,或者,如果数中所有的都比要找的小,它就是a.size()

如果数里面有重元素,那它不能保会返回哪一个。个算法不支持重元素,不它也不报错。所以,如果你需要的是一个无重元素的有序序列的,那可以考使用本章后面所介TreeSet(支持【排序“sorted order”】)和LinkedHashSet(支持【插入“sorted order”】)。两个会帮你照看所有细节。只有在遇到性能瓶候,你才应该用手动维护的数来代替两个

如果排序的候用到了Comparator针对对象数primitive不允使用Comparator),那binarySearch()候,也必使用同一个Comparator(用个方法的重版)。

部分的总结

而言之,如果你要持有一组对象,首,同也是效率最高的选择应该是数。而且,如果是一primitive,你也只能用数有一些更一般的情况,也就是写程序的不知道要用多少象,或者要用一复杂方式来存储对象情况。此,Java提供了容器container class。其基本型有ListSetMap

们还有一些的特性。比方Set所持有的象,个个都不同,Map是一个关联性数associative array,它能在两个象之建立系。此外,与数不同,它们还能自动调整大小,所以你可以往里面放任意数量的象。

容器

Java2的重新设计1.01.1里面那个表的容器。新的设计凑也更合理。同它也补齐了容器类库的功能,提供了表(linked list),列(queue)和双向列(deques“decks”数据构的功能。

Java2的容器要解决持有,而它把问题分成两

1Collection:通常是一有一定律的独立元素。List按照特定的序持有些元素,而Set不能保存重的元素。(bag没有个限制,但是Java的容器类库没有实现它,因List提供这种功能了)

2Map:一--key-value)形式出pair。初看上去,它应该是一个pairCollection,但是真这么去做的,它就会得很滑稽,所以是把个概念独立列出来好。退一步说,真的要用到Map的某个自己的候,建一个Collection也是很方便的。 Map可以返回key)的”SetCollection,或者pairSet。和数Map不需要什修改,就能很容易地展成多。你只要直接把Map值设Map就可以了(然后它的再是Map,依此推)。

Java的容器分成两基本型。它的区就在,个位置能放多少象。Collection只允许每个位置上放一个象(个名字有点误导,因容器类库也常被collections)。它包括以一定序持有一组对List,以及只能允添加不重Set ArrayList是一List,而HashSet是一Set。你可以用add()方法往Collection里面加象。

Map保存的是key)--形式的pair,很像是一个微型数据

Map又被称为关联性数associative array)。你可以用put()方法往Map里面加元素。它接受--形式pair作参数。

fill()方法还为CollectionMap作了重出在默情况下使用容器toString()方法。打印出来的Collection会用方括号括起来,元素与元素之用逗号分Map会用花括号括起来,用等号起来(在左在右)。

List会老老实实地持有你所入的所有象,既不做排序也不做编辑Set则每象只接受一次,而且要用它自己的规则对元素行重新排序(一般情况下,你心的只是Set包没包括某个象,而不是它到底排在哪里--如果是那,你最好是用List)。而Map也不接收重pair,至于是不是重,要由key来决定。此外,它也有它自己的内部排序规则,不会受序影响。如果插入序是很重要的,那你就只能使用LinkedHashSet LinkedHashMap了。

填充容器

ArraysCollection也有一个叫Collections,它包含了一些静用工具方法,其中就有一个fill() fill()也只是把同一个象的reference制到整个容器,而且它只能List,不能SetMap工作。

容器的缺点:不知道象的

Java的容器有个缺点,就是往容器里面放象的候,会把象的型信息了。是因为开发容器的程序不会知道你要用它来保存什么类型的象,而容器只保存特定型的象又会影响它的通用性。所以容器被做成只有持有Object,也就是所有象的根reference这样它就能持有任何型的象了。(当然不包括primitive,因不是象,也没有象。)是一个很了不起的方案,只是:

1,由于在将象放入容器的候,它的型信息被扔掉了,所以容器能往里面加什么类型的没有限制。比方,即使你想它只持有cat人也能很易地把dog去。

2,由于象的型信息没了,容器只知道它持有的Objectreference,所以象在使用之前须进转换

好的一面是,Java不会用放容器里的象。假你往cat的容器里面扔了个dog,然后要把个容器里的所有象都当cat来用,当你把dogreferencecat的容器里面拉出来,并且试图将它转换cat候,就会引一个RuntimeException

ArrayList的用法也是很简单:先建一个,用add()象放去,要用的候再get()一个下--就跟用数差不多,只是不需要用方括号了。ArrayList也有一个size()方法,它会告你容器里面有多少象,这样你就不会粗心大意地了界然后引异常了。

即使不正确它也能运行

,即使不把转换成原先的型,它好像也能正常工作。有一情况比特殊:String能从编译器哪里得到一些能使之平工作的特殊帮助。只要编译器没能得到它所期望的String象,它就会toString()个方法油Object,能被任何Java覆写。它所返回的String象,会被用到任何要用它的地方。

于是只要覆写了toString()方法,你就能打印象了。

做一个型敏感的ArrayList

参数化型(Parameterized types

迭代器

是哪容器,你都得有法既能放西去,也能拿西出来。竟,容器的主要任就是存放象。ArrayListadd()就是用来放西的,而 get()是把象拿出来的法。ArrayList恨灵活;你可以随提取任何西,并且一个下上就能选择另一个元素。

迭代器(iterator又是一个设计模式)是一个象,它的任是,能在程序在不知道,或者不心他所理的是什么样的底序列的情况下,就能在一个象序列中前后移,并取其中的象。此外迭代器是一通常所象,既建代价很小的象。

JavaIterator就属于有这种限制的迭代器。它做不了很多事情,除了:

1,用iterator()方法叫容器传给你一个Iterator象。第一次Iteratornext()方法的候,它就会传给你序列中的第一个元素。

2,用next()方法取序列中的下一个象。

3,用hasNext()方法查询序列中是否有其他象。

4,用remove()方法除迭代器所返回的最后一个元素。

这么多了。只是迭代器的一个恨简单实现,不过还是很大(List有一个更精巧的ListIterator)。

意的递归Unintended recursion

由于Java准容器(同其它)也是Object的,因此它也有一个toString()方法。个方法已被覆写了,所以它能生成一个表示它自己以及所有它所保存的象的String。比如ArrayListtoString()方法就会遍ArrayList个元素,然后用它toString()方法。假你要打印的地址。好像最直接的法就是使用this。但是会出很多异常,解决的法就是去Object toString()方法,它就是干活的。所以不要用this应该super.toString()

容器分

根据程的需要,CollectionMap有好几个实现实际上只有三容器件--MapListSet,而每种又有两到三个实现

与存放象有的接口包Collection, List, SetMap。在理想情况下,大多数代码应该只同些接口打交道,只是在建容器的候才要精确地指明它的确切型。

add(),就像它的名字告的,会把新的元素放Collection。但是文档里面特地声明,“add()会确保容器包含指定的元素说给Set的,因它只添加原先没有的元素,ArrayList或其他Listadd()把它放,因List并不心它是不是保存了相同的元素。

Collection都能用iterator()方法生一个Iterator里,我Iterator来遍整个Collection,然后把他打印出来。

Collection的功能

下面这张出了Collection的所有功能,也就是你能用SetList做什事(不包括从Object动继来的方法)。(List有一些外的功能。)Map不是Collection的,所以我会区别对待。

boolean add(Object):确保容器能持有你传给它的那个参数。如果没有把它加去,就返回false。(是个的方法,本章稍后会再作解。)

boolean addAll(Collection):加入参数Collection所含的所有元素。只要加了元素,就返回true

void clear():清除容器所保存的所有元素。(

boolean contains(Object):如果容器持有参数Object,就返回true

boolean containsAll(Collection):如果容器持有参数Collection所含的全部元素,就返回true

boolean isEmpty():如果容器里面没有保存任何元素,就返回true

Iterator iterator():返回一个可以在容器的各元素之Iterator

boolean removeAll(Collection)除容器里面所有参数Collection所包含的元素。只要删过东西,就返回true。(

boolean retainAll(Collection):只保存参数Collection所包括的元素(集合交集的概念)。如果过变化,返回true。(

int size():返回容器所含元素的数量。

Object[] toArray():返回一个包含容器中所有元素的数

Object[] toArray(Object[] a):返回一个包含容器中所有元素的数,且个数不是普通的Object,它的应该同参数数a型相同(要做转换)。

注意,里没有能行随机访问get()方法。是因Collection包括Set。而Set有它自己的内部序(因此随即访问事毫无意的)。所以如果你要检查Collection的元素,你就必使用迭代器。

接下来List, SetMap的各种实现了,每讲容器,我都会(用星号)告你默情况下应该选用哪种实现

List的功能

List的基本用法事相当将但的。大多数候,你只是用add()象,用get()象,用iterator()个序列的Iterator,但List有一些的很有用的方法。

实际上有两List:擅长对元素行随机访问的,常用的ArrayList,和更大的LinkedListLinkedList不是快速的随机访问设计的,但是它却有一更加通用的方法。

Lisk(接口):List的最重要的特征就是有序;它会确保以一定的序保存元素。ListCollection的基上添加了大量方法,使之能在序列中插入和除元素。(只LinkedList推荐使用。)List可以制造ListIterator象,你除了能用它在List的中插入和除元素之外,能用它沿两个方法遍List

ArrayList*:一个用数组实现List。能行快速的随机访问,但是往列表中插入和除元素的候比慢。ListIterator只能用在反向遍ArrayList合,不要用它来插入和除元素,因相比LinkedList,在ArrayList里面用ListIterator的系统开销高。

LinkedList对顺访问进行了化。在List插入和除元素的代价也不高。随机访问的速度相对较慢。(用ArrayList吧。)此外它addFirst()addLast()getFirst()getLast()removeFirst()removeLast()等方法(些方法,接口和基均未定),你能把它当成stack),列(queue)或双向列(deque)来用。

住,容器只是一个存储对象的盒子。如果个笑盒子能帮你解决所有的问题,那你就用不着取管它事怎么实现的(在大多数情况下,是使用象的基本概念)。如果开发环境里面有一些的,会造成固定的性能开销的因素存在,那ArrayListLinkedList的性能差就会得不那重要了。你只需要它中的一个,你甚至可以想象有这样完美的抽象容器;它能根据用途,自地切其底实现

LinkedList做一个

stack也被称先出LIFO)的容器。就是,最后一个被进栈中的西,会第一个出来。同其他Java容器一压进去和出来的西都是Object,所以除非你只用Object的功能,否就必须对弹起来的西转换

LinkedList的方法能直接实现栈的功能,所以你完全可以不写Stack而直接使用LinkedList

如果你只想要的功能,那么继承就不太合适了,因为继承出来的是一个LinkedList的所有方法的

LinkedList做一个

列(queue)是一个先出FIFO)容器。也就是,你把一端把西放去,从另一端把西取出来。所以你放西的序也就是取西的序。LinkedList有支持列的功能的方法,所以它也能被当作Queue来用。

能很易地用LinkedList做一个deque(双向列)。它很像列,只是你可以从任意一端添加和除元素。

Set的功能

Set的接口就是Collection的,所以不像那两个List,它没有外的功能。实际Set确确实实就是一个Collection--只不方式不同了。(承和多性的完美运用:表达不同地行。)Set会拒持有多个具有相同象的例(象的又是由什决定的呢?问题较复杂,我以后会)。

Set(接口):加入Set个元素必是唯一的;否Set是不会把它加去的。要想加SetObjectequals()这样才能象的唯一性。Set的接口和Collection的一摸一Set的接口不保它会用哪种顺序来存元素。

HashSet*为优查询速度而设计Set。要放HashSet里面的Object得定hashCode()

TreeSet:是一个有序的Set,其底是一颗树这样你就能从Set里面提取一个有序序列了。

LinkedHashSet(JDK 1.4):一个在内部使用表的Set,既有HashSet查询速度,又能保存元素被加去的序(插入序)。用IteratorSet候,它是按插入访问的。

HashSet保存象的序是和TreeSetLinkedHashSet不一的。是因是用不同的方法来存找元素的。(TreeSet用了一的数据构【red-black tree data structure】来元素排序,而HashSet用了专为快速找而设计的散列函数。LinkedHashSet在内部用散列来提高查询速度,但是它看上去像是用表来保存元素的插入序的。)你写自己的候,一定要住,Set要有一个判断以什么顺序来存元素的准,也就是你必须实现 Comparable接口,并且定compareTo()方法。

SortedSet

SortedSet(只有TreeSet一个实现可用)中的元素一定是有序的。使得SortedSet接口多了一些方法:

Comparator comparator():返回Set使用的Comparator象,或者用null表示它使用Object自有的排序方法。

Object first():返回最小的元素。

Object last():返回最大的元素。

SortedSet subSet(fromElement,toElement):返回Set的子集,其中的元素从fromElement始到toElement止(包括fromElement,不包括toElement)。

SortedSet headSet(toElement):返回Set的子集,其中的元素都小于toElement

SortedSet headSet(toElement):返回Set的子集,其中的元素都大于fromElement

注意,SortedSet意思是根据象的比较顺,而不是插入行排序。

Map的功能

ArrayList你用数字在一愕嘎象序列里面选择,所以从某,它是将数字和关联起来。但是,如果你想根据其他条件在一个象序列里面选择,那又做呢?就是一个例子。它的准是取最后一个被。我常用的术语mapdictionary,或 associative array就是一非常大的,能在序列里面行挑的工具。从概念上,它看上去像是一个ArrayList,但它不用数字,而是用另一个象来象!是一重要的程技巧。

一概念在Java中表现为Mapput(Object key, Object value)方法会往Map里面加一个,并且把(你所用的象)系起来。之后,get(Object key)就会返回与之相。你也可以用containsKey()containsValue()测试Map是否包含有某个

Java类库里有好几MapHashMapTreeMapLinkedHashMapWeakHashMap,以及 IdentityHashMap。它实现Map的基本接口,但是在行方式方面有着明异。些差异体在,效率,持有和表示pair序,持有象的时间长短,以及如何决定的相等性。

性能Map所要面的一个大问题。如果你知道get()工作的,你就会发觉(比方)在ArrayList里面找象会是相当慢的。而正是 HashMap强项。它不是慢慢地一个个地找,而是用了一被称hashcode的特殊找的。散列(hash算法,它会从目标对象当中提取一些信息,然后生成一个表示象的独特int hashCode()Object的方法,因此所有Java象都能生成hash codeHashMap利用象的hashCode()行快速的找。这样性能就有了急的提高。

Map(接口):--系(既pairs),这样就能用来找

HashMap*:基于hash表的实现。(用它来代替Hashtable。)提供时间恒定的插入与查询。在构造函数可以hash表的capacityload factor。可以通构造函数来调节其性能。

LinkedHashMap(JDK 1.4):很像HashMap,但是用Iterator行遍候,它会按插入序或最先使用的序(least-recently-used (LRU)order访问。除了用Iterator外,其他情况下,只是比HashMap稍慢一点。用Iterator的情况下,由于是使用表来保存内部序,因此速度会更快。

TreeMap:基于数据构的实现。当你pair,会发现们时序(根据ComparableComparator,我们过一会)排列的。TreeMap的特点,你得到的一个有序的MapTreeMapMap中唯一有subMap()方法的实现个方法能中的一部分。

WeakHashMap:一个weak keyMap,是某些特殊问题设计的。它能Map放其所持有的象。如果某个象除了在Map当中充当之外,在其他地方都没有其reference,那它将被当作垃圾回收。

IdentityHashMap(JDK 1.4):一个用==,而不是equals()来比较键hash map。不是平常使用而设计的,是用来解决特殊问题的。

散列是往Map里存数据的常用算法。

SortedMap

SortedMap(只有TreeMap一个实现)的肯定是有序的,因此个接口里面就有一些附加功能的方法了。

Comparator comparator():返回Map所使用的comparator,如果是用Object内置的方法的返回null

Object firstKey():返回第一个

Object lastKey():返回最后一个

SortedMap subMap(fromKey, toKey):返回Map的一个子集,其fromKey始到toKey止,包括前者,不包括后者。

SortedMap headMap(toKey):返回Map的一愕嘎子集,其均小于toKey

SortedMap tailMap(fromKey):返回Map的一个子集,其均大于等于fromKey

pair是按key序存的,由于TreeMap序的概念,因此位置是有意的,所以你可以去取它的第一个和最后一个元素,以及它的子集。

LinkedHashMap

了提高速度,LinkedHashMap所有西都做了hash,而且遍候(println()会遍整个Map,所以你能看到程)会按插入序返回pair。此外,你可以在LinkedHashMap的构造函数里面行配置它使用基于访问LRUleast-recently -used)算法,这样还没被访问过的元素(同也是要除的候选对象)就会出列的最前这样为节源而写一个定清理的程序就得很简单了。

散列算法与Hash

一个合适的equals()做到一下五点:

1 反身性:任何x, x.equals(x)true的。

2 称性:任何xy,如果y.equals(x)true的,那

x.equals(y)也必true的。

3 传递性:任何xyz,如果x.equals(y)true的,且

y.equals(z)也是true的,那x.equals(z)也必true的。

4 一致性:任何xy,如果象里面用来判断相等姓的信息没有修

,那论调用多少次x.equals(y),它都必一致地返回

truefalse

5 于任何非空的xx.equals(null)返回false

Object.equals()只是简单地比两个象的地址。

如果你想把子集写的HashMap来用的,你就必hashCode()equals()都覆写了。

理解hashCode()

如果你不覆写hashCode()equals(),散列数据构(HashSetHashMapLinkedHashSet,或LinkedHashMap)就没法正确地

散列的价就在于速度:散列算法能很快地找出西。

是最快的数据构。

持有reference

java.lang.ref类库里有一套能增垃圾回收器工作的灵活性的。一旦碰到了象达到要耗光内存候,就会的格外有用。有三个承抽象Reference的:SoftReferenceWeakReferencePhantomReference。如果待理的象只能通过这Reference访问,那么这Reference象就会向垃圾回收器提供一些不同级别的暗事。

……

总结Java类库的容器

1。数象和数字形式的下标联系起来。它持有的是型确定的象,这样提取象的候就不用再作传递了。它可以是多的,也可以持有primitive。但是建之后它的容量不能改了。

2Collection持有个元素,而Map持有相关联pair

3。和数List也把数字下系起来,你可以把数List想成有序的容器。List会随元素的增加自动调整容量。但是List只能持有Object reference,所以不能存放primitive,而且把Object提取出来之后,要做传递

4。如果要做很多随机访问,那么请ArrayList,但是如果要再List的中做很多插入和除的,就应该LinkedList了。

5LinkedList能提供列,双向列和的功能。

6Map提供的不是象与数关联,而是象和象的关联

HashMap看重的是访问速度,而TreeMap各国那看重序,因而它不如HashMap快。而LinkedHashMap保持象插入的序,但是也可以用LRU算法它重新排序。

7Set只接受不重象。HashSet提供了最快的查询速度。而TreeSet保持元素有序。LinkedHashSet保持元素的插入序。

8。没必要再在新代里使用旧类库留下来的VectorHashtableStack了。

容器类库是你天都会用到的工具,它能使程序更介,更大并且更搞笑。

最近在一本J2EE中看到了很不集合框架的明文章,筛选上来和大家共享,集合框架提供管理象集合的接口和.它包含接口,,算法,以下是它的各个件的.

Collection接口

Collection是最基本的集合接口,一个Collection代表一Object,即Collection的元素(Elements)。一些Collection相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接承自CollectionJava SDK提供的都是承自Collection子接口ListSet

所有实现Collection接口的都必提供两个准的构造函数:无参数的构造函数用于建一个空的Collection,有一个Collection参数的构造函数用于建一个新的Collection个新的Collection入的Collection有相同的元素。后一个构造函数允户复制一个Collection

如何遍Collection中的一个元素?不Collection实际类型如何,它都支持一个iterator()的方法,方法返回一个迭代子,使用迭代子即可逐一访问Collection一个元素。典型的用法如下:

Iterator it =collection.iterator(); //得一个迭代子

while(it.hasNext()) {

Object obj = it.next(); // 得到下一个元素

}

Collection接口派生的两个接口是ListSet

List接口

List是有序的Collection,使用此接口能精确的控制个元素插入的位置。用使用索引(元素在List中的位置,似于数)来访问List中的元素,这类似于Java的数

和下面要提到的Set不同,List有相同的元素。

除了具有Collection接口必iterator()方法外,List提供一个listIterator()方法,返回一个ListIterator接口,和准的Iterator接口相比,ListIterator多了一些add()的方法,允添加,除,定元素,能向前或向后遍

实现List接口的常用LinkedListArrayListVectorStack

LinkedList

LinkedList实现List接口,允null元素。此外LinkedList提供外的getremoveinsert方法在LinkedList的首部或尾部。些操作使LinkedList可被用作堆stack),列(queue)或双向列(deque)。

注意LinkedList没有同方法。如果多个线程同时访问一个List自己实现访问。一解决方法是在List构造一个同List

List list =Collections.synchronizedList(new LinkedList(…));

ArrayList

ArrayList实现了可大小的数。它允所有元素,包括nullArrayList没有同

sizeisEmptygetset方法运行时间为常数。但是add方法开销为的常数,添加n个元素需要O(n)时间。其他的方法运行时间为线性。

ArrayList例都有一个容量(Capacity),即用于存元素的数的大小。个容量可随着不断添加新元素而自增加,但是增算法并没有定。当需要插入大量元素,在插入前可以ensureCapacity方法来增加ArrayList的容量以提高插入效率。

LinkedListArrayList也是非同的(unsynchronized)。

Vector

Vector非常ArrayList,但是Vector是同的。由Vector建的Iterator然和ArrayList建的Iterator是同一接口,但是,因Vector是同的,当一个Iterator建而且正在被使用,另一个线程改Vector的状(例如,添加或除了一些元素),这时调Iterator的方法将抛出ConcurrentModificationException,因此必获该异常。

Stack

Stack承自Vector实现一个后先出的堆Stack提供5外的方法使得Vector得以被当作堆使用。基本的pushpop方法,peek方法得到栈顶的元素,empty方法测试是否空,search方法检测一个元素在堆中的位置。Stack刚创建后是空

Set接口

Set是一不包含重的元素的Collection,即任意的两个元素e1e2都有e1.equals(e2)=falseSet最多有一个null元素。

很明Set的构造函数有一个束条件,入的Collection参数不能包含重的元素。

注意:必小心操作可变对象(Mutable Object)。如果一个Set中的可元素改了自身状态导Object.equals(Object)=true致一些问题

Map接口

注意,Map没有Collection接口,Map提供keyvalue的映射。一个Map中不能包含相同的keykey只能映射一个valueMap接口提供3集合的视图Map的内容可以被当作一key集合,一value集合,或者一key-value映射。

Hashtable

HashtableMap接口,实现一个key-value映射的哈希表。任何非空(non-null)的象都可作key或者value

添加数据使用put(key, value),取出数据使用get(key)两个基本操作的时间开销为常数。

Hashtableinitialcapacityload factor两个参数整性能。通常缺省的load factor 0.75好地实现时间和空的均衡。增大load factor可以省空但相时间将增大,会影响像getput这样的操作。

使用Hashtable简单示例如下,将123放到Hashtable中,他key”one””two””three”

Hashtable numbers = newHashtable();

numbers.put(“one”, newInteger(1));

numbers.put(“two”, newInteger(2));

numbers.put(“three”, newInteger(3));

要取出一个数,比如2,用相key

Integer n =(Integer)numbers.get(“two”);

System.out.println(“two = ” +n);

由于作key象将通过计算其散列函数来确定与之对应value的位置,因此任何作key象都必须实现hashCodeequals方法。hashCodeequals方法承自根Object,如果你用自定当作key,要相当小心,按照散列函数的定,如果两个象相同,即obj1.equals(obj2)=truehashCode相同,但如果两个象不同,hashCode不一定不同,如果两个不同象的hashCode相同,这种现象称冲突,冲突会致操作哈希表的时间开销增大,所以尽量定好的hashCode()方法,能加快哈希表的操作。

如果相同的象有不同的hashCode哈希表的操作会出意想不到的果(期待的get方法返回null),要避免这种问题,只需要牢一条:要同时复equals方法和hashCode方法,而不要只写其中一个。

Hashtable是同的。

HashMap

HashMapHashtable似,不同之在于HashMap是非同的,并且允null,即null valuenull key。,但是将HashMap视为Collectionvalues()方法可返回Collection),其迭代子操作时间开销HashMap的容量成比例。因此,如果迭代操作的性能相当重要的,不要将HashMap的初始化容量高,或者load factor低。

WeakHashMap

WeakHashMap是一HashMap,它key弱引用,如果一个key不再被外部所引用,那么该key可以被GC回收。

总结

如果及到堆列等操作,应该List于需要快速插入,除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList

如果程序在单线境中,或者访问仅仅在一个线程中行,考非同,其效率高,如果多个线程可能同操作一个应该使用同

要特注意哈希表的操作,作key象要正确equalshashCode方法。

尽量返回接口而非实际型,如返回List而非ArrayList这样如果以后需要将ArrayListLinkedList,客端代不用改就是针对抽象程。

Java言内置的型,除此之外,Java有多保存象引用的方式。Java类库提供了一套相当完整的容器,使用的方法可以保存和操纵对象。下面分别进讨论,在研究Java容器之前,先了解一下Java的基本功能和特性
1.  
的基本特
与其它种类的容器(List/Set/Map)的区在于效率、确定的型和保存基本型数据的能力。数是一高效的存和随机访问对象引用序列的方式,使用数可以快速的访问中的元素。但是当建一个数组对(注意和象数的区)后,数的大小也就固定了,当数不足的候就再建一个新的数,把旧的数中所有的引用制到新的数
Java
中的数和容器都需要检查,如果越界就会得到一个RuntimeException异常。点和C++中有所不同,C++vector的操作符[]不会做检查在速度上会有一定的提高,Java的数和容器会因为时刻存在的检查带来一些性能上的开销
Java
中通用的容器不会以具体的型来象,容器中的象都是以Object理的,Java中所有的基。另外,数可以保存基本型,而容器不能,它只能保存任意的Java
一般情况下,考到效率与检查应该尽可能考使用数。如果要解决一般化的问题,数可能会受到一些限制,这时可以使用Java提供的容器
2.  
操作数用功
java.util.Arrays中,有static方法,提供了操作数的一些基本功能
equals()
方法—-用于比两个数是否相等,相等的条件是两个数的元素个数必相等,并且对应位置的元素也相等
fill()
方法—-用以某个填充整个数个方法有点笨
asList()
方法—-接受任意的数组为参数,将其转变为List容器。
binarySearch()
方法—-用于在已排序的数找元素,需要注意的是必是已排序的数。当Arrays.binarySearch()找到了找目标时方法将返回一个等于或大于0,否将返回一个负值,表示在目前的排序状下此目元素所应该插入的位置。负值算公式是“-x-1”x指的是第一个大于象的元素在数中的位置,如果数中所有的元素都小于要找的象,x = a.size()。如果数中包含重的元素,无法保找到的是哪一个元素,如果需要没有重元素的数排序,可以使用TreeSet或者LinkedHashSet。另外,如果使用Comparator排序了某个象数,在使用方法提供同Comparator型的参数。需要注意的是,基本型数无法使用Comparator行排序
sort()
方法—-组进行升序排序
Java类库中,另有static方法System.arraycopy()用来制数,它针对所有型做了重

3.  的排
Java1.01.1两个版本中,类库缺少基本的算法操作,包括排序的操作,Java2行了改善。在行排序的操作,需要根据象的实际类行比操作,如果为每种不同的型各自写一个不同的排序方法,将会使得代用。一般的程序设计标应将保持不的事物与会的事物相分离。在里,不的是通用的排序算法,化的是各种对象相互比的方式
Java
有两方式来实现的功能,一实现java.lang.Comparable接口,接口只有一个compareTo()方法,并以一个Object类为参数,如果当前象小于参数返回负值,如果相等返回零,如果当前象大于参数返回正。另一方法是采用策略(strategy)设计模式,将会化的代封装在它自己的(策略)中,再将策略象交保持不的代中,后者使用此策略实现它的算法。因此,可以不同的比方式生成不同的象,将它用在同的排序程序中。在此情况下,通一个实现Comparator接口的建了一个策略,个策略compare()equals()两个方法,一般情况下实现compare()方法即可。
使用上述两方法即可任意基本型的数组进行排序,也可以任意的象数组进行排序。再提示一遍,基本型数无法使用Comparator行排序
Java
类库中的排序算法针对排序的行了——针对基本设计快速排序针对对设计并排序。一般不用担心其性能。

Java容器分析–ListSet
容器可以大大提高程效率和程能力,在Java2中,所有的容器都由SUN公司的Joshua Bloch行了重新设计,丰富了容器类库的功能
Java2
容器类类库的用途是保存,它分
Collection—-
独立的元素,通常些元素都服从某种规则List保持元素特定的序,而Set不能有重元素
Map—-
键值对象,即其元素是成象,最典型的用就是数据字典,并且有其它广泛的用。另外,Map可以返回其所有键组成的Set和其所有值组成的Collection,或其键值对组成的Set,并且可以像数样扩展多Map,只要Map键值对是一个Map即可。
1.
迭代器
迭代器是一种设计模式,它是一个象,它可以遍选择序列中的象,而开发不需要了解序列的底层结构。迭代器通常被称象,因为创建它的代价小
Java
中的Iterator功能比较简单,并且只能向移
(1)    
使用方法iterator()要求容器返回一个Iterator。第一次Iteratornext()方法,它返回序列的第一个元素
(2)    
使用next()得序列中的下一个元素
(3)    
使用hasNext()检查序列中是否有元素
(4)    
使用remove()将迭代器新返回的元素
Iterator
Java迭代器最简单实现List设计ListIterator具有更多的功能,它可以从两个方向遍List,也可以从List中插入和除元素
2.List
的功能方法
List(interface):
次序是List最重要的特点;它确保维护元素特定的序。ListCollection添加了多方法,使得能List插入与移除元素(只推荐LinkedList使用)。一个List可以生成ListIterator,使用它可以从两个方向遍List,也可以从List插入和除元素
ArrayList:
由数组实现List。它允许对元素行快速随机访问,但是向List插入与移除元素的速度很慢。ListIterator应该来由后向前遍ArrayList,而不是用来插入和除元素,因为这LinkedList开销要大很多
LinkedList:
对顺访问进行了化,向List插入与除得开销不大,随机访问则对较(可用ArrayList代替)。它具有方法addFirst()addLast()getFirst()getLast()removeFirst()removeLast()些方法(没有在任何接口或基中定义过)使得LinkedList可以当作堆列和双向列使用
3.Set
的功能方法
Set(interface):
存入Set个元素必是唯一的,因Set不保存重元素。加入SetObjectequals()方法以确保象的唯一性。SetCollection有完全一的接口。Set接口不保证维护元素的次序
HashSet:
快速找而设计Set。存入HashSet象必hashCode()
TreeSet:
保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。
LinkedHashSet:
具有HashSet查询速度,且内部使用维护元素的(插入的次序)。于是在使用迭代器遍Set果会按元素插入的次序
HashSet
采用散列函数元素行排序,专门为快速查询设计的;TreeSet采用的数据行排序元素;LinkedHashSet内部使用散列以加快查询速度,同使用维护元素的次序,使得看起来元素是以插入的序保存的。需要注意的是,生成自己的类时Set需要维护元素的存储顺序,因此要实现Comparable接口并定compareTo()方法。

Java容器分析–Map

准的Java类库中包含了几种类型的Map,它有同的基本接口Map,但是行特性各不相同,主要表在效率、键值对的保存、元素呈次序、象的保存周期和判定是否等价的策略等方面
1.Map
的功能方法
Map(interface):
维护labelvalue关联性,使得可以通labelvalue
HashMap: Map
基于散列表的实现,取代了Hashtable。插入和查询label/value开销是固定的,并且可以通构造器置容量和负载因子,以整容器的性能
LinkedHashMap:
HashMap的基上做了一些,在迭代遍,取得label/value序是其插入的次序,或者是最近最少使用(LRU)的次序,速度上比HashMap要慢一点,但在迭代访问时速度会更快,主要原因是它使用了维护内部次序
TreeMap:
labellabel/value,元素会被排序,其次序由ComparableComparator决定,因此查询所得到的果是经过排序的。另外,它是唯一subMap()方法的Map具体,即返回一个子。它也是SortedMap接口的唯一实现subMap()方法也是从接口的。
WeakHashMap: Weak Key
映射,允许释放映射所指向的象。当映射之外没有引用指向某个label,此label可以被垃圾收集器回收。
IdentityHashMap:
使用==代替equals()label行比的散列映射
2.hashCode()
当使用中的IntegerHashMaplabel,程序能正常运行,但是使用自己建的HashMaplabel,通常犯一个错误
HashMap中通labelvalue实际上是label象地址的散列来确定value的。一般情况下,我是使用基Object的方法hashCode()来生成散列,它默是使用象的地址来算的,因此由第一个new Apple(5)和第二个new Apple(5)生成的散列是不同的,不能完成正确的找。通常,我可以写自己的hashCode()方法来覆盖基的原始方法,但与此同,我时实现equals()方法来判断当前的label是否与表中存在的label相同。正确的equals()方法足五个条件
(1)    
自反性。于任意的xx.equals(x)一定返回true
(2)    
称性。于任意的xy,如果y.equals(x)返回truex.equals(y)也返回true
(3)    
传递性。于任意的xyz,如果有x.equals(y)返回truey.equals(z)返回truex.equals(z)一定返回true
(4)    
一致性。于任意的xy,如果象中用于等价比的信息没有改,那论调x.equals(y)多少次,返回的应该保持一致,要一直是true,要一直是false
(5)    
任何不是nullxx.equals(null)一定返回false
equals()
的是象的地址,如果要使用自己的HashMaplabel,必hashCode()equals()方法。
使用散列的目的:想要使用一个象来找另一个象。使用TreeSetTreeMap也能实现此目的。另外,可以自己实现一个Map,此,必提供Map.entrySet()方法来生成Map.Entry象的Set
使用散列的价:速度,散列使得查询可以快速行。散列将label保存中方便快速查询,因元素最快的数据构是数,用它来表示label的信息(后面有信息的描述),而不是label本身。通label算得到一个数字,作的下个数字就是散列(即前面所述的信息)散列具体是通在基Object中,可能由程序自定覆盖的hashCode()方法,即散列函数生成。了解决数容量来的限制,可以使不同的label生成相同的下,保存在一个list中,一个表就是数的一个元素。查询label就可以通过对list中的信息找,当散列函数比好,数个位置中的list短,可以快速找到数元素list中的某个位置,提高了整体速度。
散列表中的slot通常称bucket了使散列分均匀,bucket一般取数。但事实证明,实际上并不是散列bucket的理想容量,近来Java散列实现都使用2,具体如何验证以后再
3.HashMap
的性能因子
容量(capacity):散列表中bucket的数量。
初始化容量(initial capacity):建散列表bucket的数量。可以在构造方法中指定HashMapHashSet的初始化容量。
尺寸(size): 散列表中记录的数量。(的元素个数,非list中元素)
负载因子(load factor):尺寸/容量。负载因子0,表示空的散列表,0.5表示半的散列表。轻负载的散列表具有冲突少,适宜插入与查询的特点,但是使用迭代器遍会比慢。高的负载会减少所需空大小。当负载达到指定值时,容器会自成倍地增加容量,并将原有的象重新分配,存入新的bucket中,程称重散列
4.
重写hashCode()关键
(1)    
同一个hashCode()应该生成同
(2)     hashCode()
方法不要依象中易的数据,当数据hashCode()就会生成一个不同的散列,即生了一个不同的label
(3)     hashCode()
于具有唯一性的象信息,例如象地址
(4)    
散列码应该心速度,而不是唯一性,因散列不必是唯一的
(5)    
好的hashCode()应该产生分均匀的散列。在Effective Java(Addison-Wesley 2001)中,Joshua BlochhashCode()出了设计,可以参考
写正确高效的hashCode()equals()可以参考ApacheJakarta Commons目中的工具。

java集合类总结 象的集合 如果程序的象数量有限,且寿命可知,那么这个程序是相当简单的。数与其它容器的区在三个方面:效率,识别以及可以持有primitives。数Java提供的,能随机存访问reference序列的多方法中的,最高效的一。数是一个简单线性序列,所有它可以快速的访问其中的元素。但是速度是有代价的;当你建了一个数之后,它的容量就固定了,而且在其生命周期里不能改。也你会提建一个数,等到快不用的候,再建一个新的,然后将旧的数里的reference全部到新的里面。其(我以后会的)ArrayList就是这么做的。但是这种灵活性所来的开销,使得ArrayList的效率比起数有了明下降。Java和容器都做检查;如果了界,它旧会一个RuntimeException这种异常表明错误是由程序造成的,这样你就用不着再在程序里面检查了。有一些泛型容器包括ListSetMap。他们处象的候就好像象都没有自己的具体型一。也就是,容器将它所含的元素都看成是(Java中所有的根Object的。这样你只需要建一容器,就能把所有型的象全都放去。从个角度来看,这种做法很不(只是苦了primitive。如果是常量,你可以用JavaprimitiveWrapper;如果是量,那就只能放在你自己的里了)。与其他泛型容器相比,里体的第二革优势建数候,你也指明了它所持有的象的型(又引出了第三点--数可以持有primitives,而容器却不行)。也就是,它会在编译候作检查,从而防止你插入错误类型的象,或者是在提取象的候把象的了。Java编译和运行都能阻止你将一个不恰当的消息传给对象。所有并不是使用容器就有什,只是如果编译器能帮你指定,那程序运行会更快,最也会少收到程序运行异常的骚扰。从效率和检查的角度来看,使用数组总是没的。但是,如果你在解决一个更一般的问题,那数就会得功能太弱了点。是第一流的 不管你用的是那种类型的数,数标识实际上都是一个建在堆(heap)里的实实在在的象的”reference实际上是那个象持有其他象的reference。你即可以用数的初始化句,含地象,也可以用new表达式,明确地象,只length属性能告你数能存多少元素。它是数组对象的一部分(实际上也是你唯一能访问的属性或方法)。‘[]’法是另一条访问组对象的途径。你没法知道数里面究竟放了多少元素,因length只是告你数能放多少元素,也就是数组对象的容量,而不是它真正已持有的元素的数量。但是,建数组对象的候,它所持有的reference都会被自地初始化null,所以你可以通过检查的某个槽位是否null,来判断它是否持有象。以此推,primitive的数,会自来数字初始化零,字符初始化(char)0boolean初始化falseprimitive容器容器只能持有Object象的reference。而数除了能持有Objectsreference之外,可以直接持有primitive。当然可以使用IntegerDoublewrapper。把primitive放到容器中,淡这样总有点怪怪的。此外,primitive的效率要比wrapper容器的高出多。当然,如果你使用primitive候,需要那能随需要自动扩展的容器的灵活性,那就不能用数了。你只能用容器来存primitivewrapper。返回一个数你写了一个方法,它返回的不是一个而是一组东西。那Java中就可以返回的就是一个数。与C++不同,你永也不必Java的数操心--只要你需要它,它就在;一旦你用完了,垃圾回收器会帮你把它打Arraysjava.util里面有一个Arrays,它包括了一可用于数static方法,些方法都是一些用工具。其中有四个基本方法:用来比两个数是否相等的equals();用来填充的fill();用来组进行排序的sort();以及用于在一个已排序的数找元素的binarySearch()。所有些方法都primitiveObject行了重。此外有一个asList()方法,它接受一个数,然后把它成一个List容器。Arrays是有用的,但它的功能并不完整。例来,如果它能不用写for就能直接打印数,那就好了。此外,正如你所看到的fill()只能用一个填数。所以,如果你想把随即生成的数字填fill()是无能力的。制一个数Java类库提供了一个System.arraycopy()static方法。相比for,它能以更快的速度拷System.arraycopy()所有型都作了重象数primitive都能拷。但是如果你拷的是象数你只拷了它reference--象本身不会被拷被成浅拷shallowcopy)。的比较为了能比是否完全相等,Arrays提供了equals()方法。当然,也是针对primitive以及Object的。两个数要想完全相等,他有相同数量的元素,而且数个元素必与另一个数的相对应的位置上的元素相等。元素的相等姓,用equals()判断。( primitive,它会使用其wrapperequals();比如int使用Integer.equals()。)。数元素的比Java里面有两实现功能的方法。一是实现java.lang.Comparable接口,并以此实现类自有的方法。是一个很简单的接口,它只有一个方法compareTo()个方法能接受另一个象作参数,如果象比参数小,它就会返回一个数,如果相同返回零,如果有的象比参数大,它就返回一个正数。static randInt()方法会生成一个介于0100的正数。在架,有人你一个没有实现Comparable接口的,或者类实现Comparable接口,但是你发现它的工作方式不是你所希望的,于是要重新定一个新的比方法。Java没有求你一定要把比进类里,它的解决方案是使用策略模式(strategy design pattern。有了策略之后,你就能把会的代封装到它自己的里(即所的策略strategy object)。你把策略象交不会的代,然后用它运用策略完成整个算法。这样,你就可以用不同的策略象来表示不同的比方法,然后把它都交同一个排序程序了。接下来就要过实现Comparator接口来定策略象了。个接口有两个方法compare()equals()。但是除非是有特殊的性能要求,否你用不着去实现equals()。因只要是,它就都含地承自Object,而Object里面已有了一个 equals()了。所以你尽可以使用缺省的Objectequals()这样就已经满足接口的要求了。Collections专门有一个会返回与象自有的比法相反的Comparator的方法。它能很易地被用到CompType上面。Collections.reverseOrder()返回了一个Comparatorreferencecompare()方法会根据第一个参数是小于,等于是大于第二个参数,分返回整数,零或是正整数。数的排序 有了内置的排序方法之后,你就能任何数排序了,不primitive象数的,只要它实现Comparable接口或有一个与之相Comparator象就行了。Java类库所用的排序算法已作了化--primitive,它用的是快速排序(Quicksort对对象,它用的是定合并排序(stable merge sort。所以除非是prolier表明排序算法是瓶,否你不用性能担心。查询有序数 一旦数排完序,你就能用Arrays.binarySearch()行快速查询了。但是切忌一个尚未排序的数使用binarySearch();因为这么做的果是没意的。如果Arrays.binarySearch()找到了,它就返回一个大于或等于0。否它就返回一个负值,而负值要表达的意思是,如果你手动维护这个数值应该插在哪个止。就是:-(插入点)-1“插入点就是,在所有比要找的那个更大中,最小的那个的下,或者,如果数中所有的都比要找的小,它就是a.size()。如果数里面有重元素,那它不能保会返回哪一个。个算法不支持重元素,不它也不报错。所以,如果你需要的是一个无重元素的有序序列的,那可以考使用本章后面所介TreeSet(支持【排序“sorted order”】)和LinkedHashSet(支持【插入“sorted order”】)。两个会帮你照看所有细节。只有在遇到性能瓶候,你才应该用手动维护的数来代替两个。如果排序的候用到了Comparator针对对象数primitive不允使用Comparator),那binarySearch()候,也必使用同一个Comparator(用个方法的重版)。数部分的总结 而言之,如果你要持有一组对象,首,同也是效率最高的选择应该是数。而且,如果是一primitive,你也只能用数有一些更一般的情况,也就是写程序的不知道要用多少象,或者要用一复杂方式来存储对象情况。此,Java提供了容器container class。其基本ListSetMap。它们还有一些的特性。比方Set所持有的象,个个都不同,Map是一个关联性数associative array,它能在两个象之建立系。此外,与数不同,它们还能自动调整大小,所以你可以往里面放任意数量的象。容器Java2的重新设计1.01.1里面那个表的容器。新的设计凑也更合理。同它也补齐了容器类库的功能,提供了表(linked list),列(queue)和双向列(deques“decks”数据构的功能。Java2的容器要解决持有,而它把问题分成两1Collection:通常是一有一定律的独立元素。List按照特定的序持有些元素,而Set不能保存重的元素。(bag没有个限制,但是Java的容器类库没有实现它,因List提供这种功能了)2Map:一--key-value)形式出pair。初看上去,它应该是一个pairCollection,但是真这么去做的,它就会得很滑稽,所以是把个概念独立列出来好。退一步说,真的要用到Map的某个自己的候,建一个Collection也是很方便的。 Map可以返回key)的”SetCollection,或者pairSet。和数Map不需要什修改,就能很容易地展成多。你只要直接把Map值设Map就可以了(然后它的再是Map,依此推)。Java的容器分成两基本型。它的区就在,个位置能放多少象。Collection只允许每个位置上放一个象(个名字有点误导,因容器类库也常被collections)。它包括以一定序持有一组对List,以及只能允添加不重Set ArrayList是一List,而HashSet是一Set。你可以用add()方法往Collection里面加象。Map保存的是key)--形式的pair,很像是一个微型数据Map又被称为关联性数associative array)。你可以用put()方法往Map里面加元素。它接受--形式pair作参数。fill()方法还为CollectionMap作了重出在默情况下使用容器toString()方法。打印出来的Collection会用方括号括起来,元素与元素之用逗号分Map会用花括号括起来,用等号起来(在左在右)。List会老老实实地持有你所入的所有象,既不做排序也不做编辑Set则每象只接受一次,而且要用它自己的规则对元素行重新排序(一般情况下,你心的只是Set包没包括某个象,而不是它到底排在哪里--如果是那,你最好是用List)。而Map也不接收重pair,至于是不是重,要由key来决定。此外,它也有它自己的内部排序规则,不会受序影响。如果插入序是很重要的,那你就只能使用LinkedHashSet LinkedHashMap了。填充容器和ArraysCollection也有一个叫Collections,它包含了一些静用工具方法,其中就有一个fill() fill()也只是把同一个象的reference制到整个容器,而且它只能List,不能SetMap工作。容器的缺点:不知道象的Java的容器有个缺点,就是往容器里面放象的候,会把象的型信息了。是因为开发容器的程序不会知道你要用它来保存什么类型的象,而容器只保存特定型的象又会影响它的通用性。所以容器被做成只有持有Object,也就是所有象的根reference这样它就能持有任何型的象了。(当然不包括primitive,因不是象,也没有象。)是一个很了不起的方案,只是:1,由于在将象放入容器的候,它的型信息被扔掉了,所以容器能往里面加什么类型的没有限制。比方,即使你想它只持有cat人也能很易地把dog去。2,由于象的型信息没了,容器只知道它持有的Objectreference,所以象在使用之前须进转换。好的一面是,Java不会用放容器里的象。假你往cat的容器里面扔了个dog,然后要把个容器里的所有象都当cat来用,当你把dogreferencecat的容器里面拉出来,并且试图将它转换cat候,就会引一个RuntimeExceptionArrayList的用法也是很简单:先建一个,用add()象放去,要用的候再get()一个下--就跟用数差不多,只是不需要用方括号了。ArrayList也有一个size()方法,它会告你容器里面有多少象,这样你就不会粗心大意地了界然后引异常了。有即使不正确它也能运行有,即使不把转换成原先的型,它好像也能正常工作。有一情况比特殊:String能从编译器哪里得到一些能使之平工作的特殊帮助。只要编译器没能得到它所期望的String象,它就会toString()个方法油Object,能被任何Java覆写。它所返回的String象,会被用到任何要用它的地方。于是只要覆写了toString()方法,你就能打印象了。做一个型敏感的ArrayList参数化Parameterized types)迭代器无是哪容器,你都得有法既能放西去,也能拿西出来。竟,容器的主要任就是存放象。ArrayListadd()就是用来放西的,而 get()是把象拿出来的法。ArrayList恨灵活;你可以随提取任何西,并且一个下上就能选择另一个元素。迭代器(iterator又是一个设计模式)是一个象,它的任是,能在程序在不知道,或者不心他所理的是什么样的底序列的情况下,就能在一个象序列中前后移,并取其中的象。此外迭代器是一通常所象,既建代价很小的象。JavaIterator就属于有这种限制的迭代器。它做不了很多事情,除了:1,用iterator()方法叫容器传给你一个Iterator象。第一次Iteratornext()方法的候,它就会传给你序列中的第一个元素。2,用next()方法取序列中的下一个象。3,用hasNext()方法查询序列中是否有其他象。4,用remove()方法除迭代器所返回的最后一个元素。就这么多了。只是迭代器的一个恨简单实现,不过还是很大(List有一个更精巧的ListIterator)。不意的递归Unintended recursion)由于Java准容器(同其它)也是Object的,因此它也有一个toString()方法。个方法已被覆写了,所以它能生成一个表示它自己以及所有它所保存的象的String。比如ArrayListtoString()方法就会遍ArrayList个元素,然后用它toString()方法。假你要打印的地址。好像最直接的法就是使用this。但是会出多异常,解决的法就是去Object toString()方法,它就是干活的。所以不要用this应该super.toString()。容器分学根据程的需要,CollectionMap有好几个实现实际上只有三容器件--MapListSet,而每种又有两到三个实现。与存放象有的接口包括Collection, List, SetMap。在理想情况下,大多数代码应该只同些接口打交道,只是在建容器的候才要精确地指明它的确切型。add(),就像它的名字告的,会把新的元素放Collection。但是文档里面特地声明,“add()会确保容器包含指定的元素说给Set的,因它只添加原先没有的元素,ArrayList或其他Listadd()把它放,因List并不心它是不是保存了相同的元素。Collection都能用iterator()方法生一个Iterator里,我Iterator来遍整个Collection,然后把他打印出来。Collection的功能下面这张出了Collection的所有功能,也就是你能用SetList做什事(不包括从Object动继来的方法)。(List有一些外的功能。)Map不是Collection的,所以我会区别对待。boolean add(Object):确保容器能持有你传给它的那个参数。如果没有把它加去,就返回false。(是个的方法,本章稍后会再作解。)boolean addAll(Collection):加入参数Collection所含的所有元素。只要加了元素,就返回truevoid clear():清除容器所保存的所有元素。(boolean contains(Object):如果容器持有参数Object,就返回trueboolean containsAll(Collection):如果容器持有参数Collection所含的全部元素,就返回trueboolean isEmpty():如果容器里面没有保存任何元素,就返回trueIterator iterator():返回一个可以在容器的各元素之Iteratorboolean removeAll(Collection)除容器里面所有参数Collection所包含的元素。只要删过东西,就返回true。(boolean retainAll(Collection):只保存参数Collection所包括的元素(集合交集的概念)。如果过变化,返回true。(int size():返回容器所含元素的数量。Object[] toArray():返回一个包含容器中所有元素的数Object[] toArray(Object[] a):返回一个包含容器中所有元素的数,且个数不是普通的Object,它的应该同参数数a型相同(要做转换)。注意里没有能行随机访问get()方法。是因Collection包括Set。而Set有它自己的内部序(因此随即访问事毫无意的)。所以如果你要检查Collection的元素,你就必使用迭代器。接下来List,SetMap的各种实现了,每讲容器,我都会(用星号)告你默情况下应该选用哪种实现List的功能List的基本用法事相当将但的。大多数候,你只是用add()象,用get()象,用iterator()个序列的Iterator,但List有一些的很有用的方法。实际上有两List:擅长对元素行随机访问的,常用的ArrayList,和更大的LinkedListLinkedList不是快速的随机访问设计的,但是它却有一更加通用的方法。Lisk(接口):List的最重要的特征就是有序;它会确保以一定的序保存元素。ListCollection的基上添加了大量方法,使之能在序列中插入和除元素。(只LinkedList推荐使用。)List可以制造ListIterator象,你除了能用它在List的中插入和除元素之外,能用它沿两个方法遍ListArrayList*:一个用数组实现List。能行快速的随机访问,但是往列表中插入和除元素的候比慢。ListIterator只能用在反向遍ArrayList合,不要用它来插入和除元素,因相比LinkedList,在ArrayList里面用ListIterator的系统开销高。LinkedList对顺访问进行了化。在List插入和除元素的代价也不高。随机访问的速度相对较慢。(用ArrayList吧。)此外它addFirst()addLast()getFirst()getLast()removeFirst()removeLast()等方法(些方法,接口和基均未定),你能把它当成stack),列(queue)或双向列(deque)来用。住,容器只是一个存储对象的盒子。如果个笑盒子能帮你解决所有的问题,那你就用不着取管它事怎么实现的(在大多数情况下,是使用象的基本概念)。如果开发环境里面有一些的,会造成固定的性能开销的因素存在,那ArrayListLinkedList的性能差就会得不那重要了。你只需要它中的一个,你甚至可以想象有这样完美的抽象容器;它能根据用途,自地切其底实现。用LinkedList做一个stack也被称先出LIFO)的容器。就是,最后一个被进栈中的西,会第一个出来。同其他Java容器一压进去和出来的西都是Object,所以除非你只用Object的功能,否就必须对弹起来的西转换LinkedList的方法能直接实现栈的功能,所以你完全可以不写Stack而直接使用LinkedList。如果你只想要的功能,那么继承就不太合适了,因为继承出来的是一个LinkedList的所有方法的。用LinkedList做一个列(queue)是一个先出FIFO)容器。也就是,你把一端把西放去,从另一端把西取出来。所以你放西的序也就是取西的序。LinkedList有支持列的功能的方法,所以它也能被当作Queue来用。能很易地用LinkedList做一个deque(双向列)。它很像列,只是你可以从任意一端添加和除元素。Set的功能Set的接口就是Collection的,所以不像那两个List,它没有外的功能。实际Set确确实实就是一个Collection--只不方式不同了。(承和多性的完美运用:表达不同地行。)Set会拒持有多个具有相同象的例(象的又是由什决定的呢?问题较复杂,我以后会)。Set(接口):加入Set个元素必是唯一的;否Set是不会把它加去的。要想加SetObjectequals()这样才能象的唯一性。Set的接口和Collection的一摸一Set的接口不保它会用哪种顺序来存元素。HashSet*为优查询速度而设计Set。要放HashSet里面的Object得定hashCode()TreeSet:是一个有序的Set,其底是一颗树这样你就能从Set里面提取一个有序序列了。LinkedHashSet(JDK 1.4):一个在内部使用表的Set,既有HashSet查询速度,又能保存元素被加去的序(插入序)。用IteratorSet候,它是按插入访问的。HashSet保存象的序是和TreeSetLinkedHashSet不一的。是因是用不同的方法来存找元素的。(TreeSet用了一的数据构【red-black tree data structure】来元素排序,而HashSet用了专为快速找而设计的散列函数。LinkedHashSet在内部用散列来提高查询速度,但是它看上去像是用表来保存元素的插入序的。)你写自己的候,一定要住,Set要有一个判断以什么顺序来存元素的准,也就是你必须实现Comparable接口,并且定compareTo()方法。SortedSetSortedSet(只有TreeSet一个实现可用)中的元素一定是有序的。使得SortedSet接口多了一些方法:Comparator comparator():返回Set使用的Comparator象,或者用null表示它使用Object自有的排序方法。Object first():返回最小的元素。Object last():返回最大的元素。SortedSet subSet(fromElement, toElement):返回Set的子集,其中的元素从fromElement始到toElement止(包括fromElement,不包括toElement)。SortedSet headSet(toElement):返回Set的子集,其中的元素都小于toElementSortedSet headSet(toElement):返回Set的子集,其中的元素都大于fromElement。注意,SortedSet意思是根据象的比较顺,而不是插入行排序。Map的功能ArrayList你用数字在一愕嘎象序列里面选择,所以从某,它是将数字和关联起来。但是,如果你想根据其他条件在一个象序列里面选择那又做呢?就是一个例子。它的准是取最后一个被。我常用的术语mapdictionary,或 associative array就是一非常大的,能在序列里面行挑的工具。从概念上,它看上去像是一个ArrayList,但它不用数字,而是用另一个象来象!是一重要的程技巧。一概念在Java中表现为Mapput(Object key, Object value)方法会往Map里面加一个,并且把(你所用的象)系起来。之后,get(Object key)就会返回与之相。你也可以用containsKey()containsValue()测试Map是否包含有某个Java类库里有好几MapHashMapTreeMapLinkedHashMapWeakHashMap,以及 IdentityHashMap。它实现Map的基本接口,但是在行方式方面有着明异。些差异体在,效率,持有和表示pair序,持有象的时间长短,以及如何决定的相等性。性能Map所要面的一个大问题。如果你知道get()工作的,你就会发觉(比方)在ArrayList里面找象会是相当慢的。而正是 HashMap强项。它不是慢慢地一个个地找,而是用了一被称hashcode的特殊找的。散列(hash算法,它会从目标对象当中提取一些信息,然后生成一个表示象的独特int hashCode()Object的方法,因此所有Java象都能生成hash codeHashMap利用象的hashCode()行快速的找。这样性能就有了急的提高。Map(接口):--系(既pairs),这样就能用来找了。HashMap*:基于hash表的实现。(用它来代替Hashtable。)提供时间恒定的插入与查询。在构造函数可以hash表的capacityload factor。可以通构造函数来调节其性能。LinkedHashMap(JDK 1.4):很像HashMap,但是用Iterator行遍候,它会按插入序或最先使用的序(least-recently-used (LRU)order访问。除了用Iterator外,其他情况下,只是比HashMap稍慢一点。用Iterator的情况下,由于是使用表来保存内部序,因此速度会更快。TreeMap:基于数据构的实现。当你pair,会发现们时序(根据ComparableComparator,我们过一会)排列的。TreeMap的特点,你得到的一个有序的MapTreeMapMap中唯一有subMap()方法的实现个方法能中的一部分。WeakHashMap:一个weak keyMap,是某些特殊问题设计的。它能Map放其所持有的象。如果某个象除了在Map当中充当之外,在其他地方都没有其reference,那它将被当作垃圾回收。IdentityHashMap(JDK 1.4):一个用==,而不是equals()来比较键hash map。不是平常使用而设计的,是用来解决特殊问题的。散列是往Map里存数据的常用算法。SortedMap SortedMap(只有TreeMap一个实现)的肯定是有序的,因此个接口里面就有一些附加功能的方法了。Comparator comparator():返回Map所使用的comparator,如果是用Object内置的方法的返回nullObject firstKey():返回第一个Object lastKey():返回最后一个SortedMap subMap(fromKey, toKey):返回Map的一个子集,其fromKey始到toKey止,包括前者,不包括后者。SortedMap headMap(toKey):返回Map的一愕嘎子集,其均小于toKeySortedMap tailMap(fromKey):返回Map的一个子集,其均大于等于fromKeypair是按key序存的,由于TreeMap序的概念,因此位置是有意的,所以你可以去取它的第一个和最后一个元素,以及它的子集。LinkedHashMap了提高速度,LinkedHashMap所有西都做了hash,而且遍候(println()会遍整个Map,所以你能看到程)会按插入序返回pair。此外,你可以在LinkedHashMap的构造函数里面行配置,它使用基于访问LRUleast-recently -used)算法,这样还没被访问过的元素(同也是要除的候选对象)就会出列的最前这样为节源而写一个定清理的程序就得很简单了。散列算法与Hash数一个合适的equals()做到一下五点:1反身性:任何x, x.equals(x)true的。2称性:任何xy,如果y.equals(x)true的,那x.equals(y)也必true的。3传递性:任何xyz,如果x.equals(y)true的,且y.equals(z)也是true的,那x.equals(z)也必true的。4一致性:任何xy,如果象里面用来判断相等姓的信息没有修改,那论调用多少次x.equals(y),它都必一致地返回truefalse5于任何非空的xx.equals(null)返回false。默Object.equals()只是简单地比两个象的地址。如果你想把子集写的HashMap来用的,你就必hashCode()equals()都覆写了。理解hashCode()如果你不覆写hashCode()equals(),散列数据构(HashSetHashMapLinkedHashSet,或LinkedHashMap)就没法正确地。散列的价就在于速度:散列算法能很快地找出西。数是最快的数据构。持有reference java.lang.ref类库里有一套能增垃圾回收器工作的灵活性的。一旦碰到了象达到要耗光内存候,就会的格外有用。有三个承抽象Reference的:SoftReferenceWeakReferencePhantomReference。如果待理的象只能通过这Reference访问,那么这Reference象就会向垃圾回收器提供一些不同级别的暗事。……
总结Java类库的容器 1。数象和数字形式的下标联系起来。它持有的是型确定的象,这样提取象的候就不用再作传递了。它可以是多的,也可以持有primitive。但是建之后它的容量不能改了。2Collection持有个元素,而Map持有相关联pair3。和数List也把数字下系起来,你可以把数List想成有序的容器。List会随元素的增加自动调整容量。但是List只能持有Object reference,所以不能存放primitive,而且把Object提取出来之后,要做传递4。如果要做很多随机访问,那么请ArrayList,但是如果要再List的中做很多插入和除的,就应该LinkedList了。5LinkedList能提供列,双向列和的功能。6Map提供的不是象与数关联,而是象和象的关联HashMap看重的是访问速度,而TreeMap各国那看重序,因而它不如HashMap快。而LinkedHashMap保持象插入的序,但是也可以用LRU算法它重新排序。7Set只接受不重象。HashSet提供了最快的查询速度。而TreeSet保持元素有序。LinkedHashSet保持元素的插入序。8。没必要再在新代里使用旧类库留下来的VectorHashtableStack了。容器类库是你天都会用到的工具,它能使程序更介,更大并且更搞笑。最近在一本J2EE中看到了很不集合框架的明文章,筛选上来和大家共享,集合框架提供管理象集合的接口和.它包含接口,,算法,以下是它的各个件的. Collection接口   Collection是最基本的集合接口,一个Collection代表一Object,即Collection的元素(Elements)。一些Collection相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接承自CollectionJava SDK提供的都是承自Collection子接口ListSet   所有实现Collection接口的都必提供两个准的构造函数:无参数的构造函数用于建一个空的Collection,有一个Collection参数的构造函数用于建一个新的Collection个新的Collection入的Collection有相同的元素。后一个构造函数允户复制一个Collection   如何遍Collection中的一个元素?不Collection实际类型如何,它都支持一个iterator()的方法,方法返回一个迭代子,使用迭代子即可逐一访问Collection一个元素。典型的用法如下:     Iterator it =collection.iterator(); // 得一个迭代子     while(it.hasNext()) {       Object obj = it.next(); // 得到下一个元素       由Collection接口派生的两个接口是ListSet
List
接口   List是有序的Collection,使用此接口能精确的控制个元素插入的位置。用使用索引(元素在List中的位置,似于数)来访问List中的元素,这类似于Java的数 和下面要提到的Set不同,List有相同的元素。   除了具有Collection接口必iterator()方法外,List提供一个listIterator()方法,返回一个ListIterator接口,和准的Iterator接口相比,ListIterator多了一些add()的方法,允添加,除,定元素,能向前或向后遍   实现List接口的常用LinkedListArrayListVectorStack
LinkedList
   LinkedList实现List接口,允null元素。此外LinkedList提供外的getremoveinsert方法在LinkedList的首部或尾部。些操作使LinkedList可被用作堆stack),列(queue)或双向列(deque)。   注意LinkedList没有同方法。如果多个线程同时访问一个List自己实现访问。一解决方法是在List构造一个同List     List list = Collections.synchronizedList(newLinkedList(…));
ArrayList
   ArrayList实现了可大小的数。它允所有元素,包括nullArrayList没有同 sizeisEmptygetset方法运行时间为常数。但是add方法开销为的常数,添加n个元素需要O(n)时间。其他的方法运行时间为线性。   ArrayList例都有一个容量(Capacity),即用于存元素的数的大小。个容量可随着不断添加新元素而自增加,但是增算法并没有定。当需要插入大量元素,在插入前可以ensureCapacity方法来增加ArrayList的容量以提高插入效率。   和LinkedListArrayList也是非同的(unsynchronized)。
Vector
   Vector非常ArrayList,但是Vector是同的。由Vector建的Iterator然和ArrayList建的Iterator是同一接口,但是,因Vector是同的,当一个Iterator建而且正在被使用,另一个线程改Vector的状(例如,添加或除了一些元素),这时调Iterator的方法将抛出ConcurrentModificationException,因此必获该异常
Stack
   Stack承自Vector实现一个后先出的堆Stack提供5外的方法使得Vector得以被当作堆使用。基本的pushpop方法,peek方法得到栈顶的元素,empty方法测试是否空,search方法检测一个元素在堆中的位置。Stack刚创建后是空
Set
接口   Set是一不包含重的元素的Collection,即任意的两个元素e1e2都有e1.equals(e2)=falseSet最多有一个null元素。   很明Set的构造函数有一个束条件,入的Collection参数不能包含重的元素。   注意:必小心操作可变对象(Mutable Object)。如果一个Set中的可元素改了自身状态导Object.equals(Object)=true致一些问题
Map
接口   注意,Map没有Collection接口,Map提供keyvalue的映射。一个Map中不能包含相同的keykey只能映射一个valueMap接口提供3集合的视图Map的内容可以被当作一key集合,一value集合,或者一key-value映射。
Hashtable
   HashtableMap接口,实现一个key-value映射的哈希表。任何非空(non-null)的象都可作key或者value   添加数据使用put(key, value),取出数据使用get(key)两个基本操作的时间开销为常数。 Hashtableinitial capacityload factor两个参数整性能。通常缺省的load factor 0.75好地实现时间和空的均衡。增大load factor可以省空但相时间将增大,会影响像getput这样的操作。 使用Hashtable简单示例如下,将123放到Hashtable中,他key”one””two””three”     Hashtable numbers = newHashtable();     numbers.put(“one”, new Integer(1));     numbers.put(“two”, new Integer(2));     numbers.put(“three”, new Integer(3));   要取出一个数,比如2,用相key     Integer n = (Integer)numbers.get(“two”);     System.out.println(“two = ” + n);   由于作key象将通过计算其散列函数来确定与之对应value的位置,因此任何作key象都必须实现hashCodeequals方法。hashCodeequals方法承自根Object,如果你用自定当作key,要相当小心,按照散列函数的定,如果两个象相同,即obj1.equals(obj2)=truehashCode相同,但如果两个象不同,hashCode不一定不同,如果两个不同象的hashCode相同,这种现象称冲突,冲突会致操作哈希表的时间开销增大,所以尽量定好的hashCode()方法,能加快哈希表的操作。   如果相同的象有不同的hashCode哈希表的操作会出意想不到的果(期待的get方法返回null),要避免这种问题,只需要牢一条:要同时复equals方法和hashCode方法,而不要只写其中一个。   Hashtable是同
HashMap
   HashMapHashtable似,不同之在于HashMap是非同的,并且允null,即null valuenull key。,但是将HashMap视为Collectionvalues()方法可返回Collection),其迭代子操作时间开销HashMap的容量成比例。因此,如果迭代操作的性能相当重要的,不要将HashMap的初始化容量高,或者load factor
WeakHashMap
   WeakHashMap是一HashMap,它key弱引用,如果一个key不再被外部所引用,那么该key可以被GC回收。
总结   如果及到堆列等操作,应该List于需要快速插入,除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList   如果程序在单线境中,或者访问仅仅在一个线程中行,考非同,其效率高,如果多个线程可能同操作一个应该使用同   要特注意哈希表的操作,作key象要正确equalshashCode方法。   尽量返回接口而非实际型,如返回List而非ArrayList这样如果以后需要将ArrayListLinkedList,客端代不用改就是针对抽象

Java言内置的型,除此之外,Java有多保存象引用的方式。Java类库提供了一套相当完整的容器,使用的方法可以保存和操纵对象。下面分别进讨论,在研究Java容器之前,先了解一下Java的基本功能和特性。

1.  的基本特性

与其它种类的容器(List/Set/Map)的区在于效率、确定的型和保存基本型数据的能力。数是一高效的存和随机访问对象引用序列的方式,使用数可以快速的访问中的元素。但是当建一个数组对(注意和象数的区)后,数的大小也就固定了,当数不足的候就再建一个新的数,把旧的数中所有的引用制到新的数中。

Java中的数和容器都需要检查,如果越界就会得到一个RuntimeException异常。点和C++中有所不同,C++vector的操作符[]不会做检查在速度上会有一定的提高,Java的数和容器会因为时刻存在的检查带来一些性能上的开销

Java中通用的容器不会以具体的型来象,容器中的象都是以Object理的,Java中所有的基。另外,数可以保存基本型,而容器不能,它只能保存任意的Java象。

一般情况下,考到效率与检查应该尽可能考使用数。如果要解决一般化的问题,数可能会受到一些限制,这时可以使用Java提供的容器

2.  操作数用功能

java.util.Arrays中,有static方法,提供了操作数的一些基本功能:

equals()方法—-用于比两个数是否相等,相等的条件是两个数的元素个数必相等,并且对应位置的元素也相等。

fill()方法—-用以某个填充整个数个方法有点笨。

asList()方法—-接受任意的数组为参数,将其转变为List容器。

binarySearch()方法—-用于在已排序的数找元素,需要注意的是必是已排序的数。当Arrays.binarySearch()找到了找目标时方法将返回一个等于或大于0,否将返回一个负值,表示在目前的排序状下此目元素所应该插入的位置。负值算公式是“-x-1”x指的是第一个大于象的元素在数中的位置,如果数中所有的元素都小于要找的象,x = a.size()。如果数中包含重的元素,无法保找到的是哪一个元素,如果需要没有重元素的数排序,可以使用TreeSet或者LinkedHashSet。另外,如果使用Comparator排序了某个象数,在使用方法提供同Comparator型的参数。需要注意的是,基本型数无法使用Comparator行排序。

sort()方法—-组进行升序排序。

Java类库中,另有static方法System.arraycopy()用来制数,它针对所有型做了重

3.  的排序

Java1.01.1两个版本中,类库缺少基本的算法操作,包括排序的操作,Java2行了改善。在行排序的操作,需要根据象的实际类行比操作,如果为每种不同的型各自写一个不同的排序方法,将会使得代用。一般的程序设计标应将保持不的事物与会的事物相分离。在里,不的是通用的排序算法,化的是各种对象相互比的方式。

Java有两方式来实现的功能,一实现java.lang.Comparable接口,接口只有一个compareTo()方法,并以一个Object类为参数,如果当前象小于参数返回负值,如果相等返回零,如果当前象大于参数返回正。另一方法是采用策略(strategy)设计模式,将会化的代封装在它自己的(策略)中,再将策略象交保持不的代中,后者使用此策略实现它的算法。因此,可以不同的比方式生成不同的象,将它用在同的排序程序中。在此情况下,通一个实现Comparator接口的建了一个策略,个策略compare()equals()两个方法,一般情况下实现compare()方法即可。

使用上述两方法即可任意基本型的数组进行排序,也可以任意的象数组进行排序。再提示一遍,基本无法使用Comparator行排序。

Java类库中的排序算法针对排序的行了——针对基本设计快速排序针对对设计并排序。一般不用担心其性能。

Java容器分析–ListSet

容器可以大大提高程效率和程能力,在Java2中,所有的容器都由SUN公司的Joshua Bloch行了重新设计,丰富了容器类库的功能。

Java2容器类类库的用途是保存,它分

Collection—-独立的元素,通常些元素都服从某种规则List保持元素特定的序,而Set不能有重元素。

Map—-键值对象,即其元素是成象,最典型的用就是数据字典,并且有其它广泛的用。另外,Map可以返回其所有键组成的Set和其所有值组成的Collection,或其键值对组成的Set,并且可以像数样扩展多Map,只要Map键值对是一个Map即可。

1.迭代器

迭代器是一种设计模式,它是一个象,它可以遍选择序列中的象,而开发不需要了解序列的底层结构。迭代器通常被称象,因为创建它的代价小。

Java中的Iterator功能比较简单,并且只能向移

(1)    使用方法iterator()要求容器返回一个Iterator。第一次Iteratornext()方法,它返回序列的第一个元素。

(2)    使用next()得序列中的下一个元素。

(3)    使用hasNext()检查序列中是否有元素。

(4)    使用remove()将迭代器新返回的元素除。

IteratorJava迭代器最简单实现List设计ListIterator具有更多的功能,它可以从两个方向遍List,也可以从List中插入和除元素。

2.List的功能方法

List(interface): 次序是List最重要的特点;它确保维护元素特定的序。ListCollection添加了多方法,使得能List插入与移除元素(只推荐LinkedList使用)。一个List可以生成ListIterator,使用它可以从两个方向遍List,也可以从List插入和除元素。

ArrayList: 由数组实现List。它允许对元素行快速随机访问,但是向List插入与移除元素的速度很慢。ListIterator应该用来由后向前遍ArrayList,而不是用来插入和除元素,因为这LinkedList开销要大很多。

LinkedList: 对顺访问进行了化,向List插入与除得开销不大,随机访问则对较(可用ArrayList代替)。它具有方法addFirst()addLast()getFirst()getLast()removeFirst()removeLast()些方法(没有在任何接口或基中定义过)使得LinkedList可以当作堆列和双向列使用。

3.Set的功能方法

Set(interface): 存入Set个元素必是唯一的,因Set不保存重元素。加入SetObjectequals()方法以确保象的唯一性。SetCollection有完全一的接口。Set接口不保证维护元素的次序。

HashSet: 快速找而设计Set。存入HashSet象必hashCode()

TreeSet: 保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。

LinkedHashSet: 具有HashSet查询速度,且内部使用维护元素的(插入的次序)。于是在使用迭代器遍Set果会按元素插入的次序示。

HashSet采用散列函数元素行排序,专门为快速查询设计的;TreeSet采用的数据行排序元素;LinkedHashSet内部使用散列以加快查询速度,同使用维护元素的次序,使得看起来元素是以插入的序保存的。需要注意的是,生成自己的类时Set需要维护元素的存储顺序,因此要实现Comparable接口并定compareTo()方法。

Java容器分析–Map

准的Java类库中包含了几种类型的Map,它有同的基本接口Map,但是行特性各不相同,主要表在效率、键值对的保存、元素呈次序、象的保存周期和判定是否等价的策略等方面。

1.Map的功能方法

Map(interface): 维护labelvalue关联性,使得可以通labelvalue

HashMap: Map基于散列表的实现,取代了Hashtable。插入和查询label/value开销是固定的,并且可以通构造器置容量和负载子,以整容器的性能。

LinkedHashMap: HashMap的基上做了一些改,在迭代遍,取得label/value序是其插入的次序,或者是最近最少使用(LRU)的次序,速度上比HashMap要慢一点,但在迭代访问时速度会更快,主要原因是它使用了维护内部次序。

TreeMap: labellabel/value,元素会被排序,其次序由ComparableComparator决定,因此查询所得到的果是经过排序的。另外,它是唯一subMap()方法的Map具体,即返回一个子。它也是SortedMap接口的唯一实现subMap()方法也是从接口承的。

WeakHashMap: Weak Key映射,允许释放映射所指向的象。当映射之外没有引用指向某个label,此label可以被垃圾收集器回收。

IdentityHashMap: 使用==代替equals()label行比的散列映射。

2.hashCode()

当使用中的IntegerHashMaplabel,程序能正常运行,但是使用自己建的HashMaplabel,通常犯一个错误

HashMap中通labelvalue实际上是label象地址的散列来确定value的。一般情况下,我是使用基Object的方法hashCode()来生成散列,它默是使用象的地址来算的,因此由第一个new Apple(5)和第二个new Apple(5)生成的散列是不同的,不能完成正确的找。通常,我可以写自己的hashCode()方法来覆盖基的原始方法,但与此同,我时实现equals()方法来判断当前的label是否与表中存在的label相同。正确的equals()方法足五个条件:

(1)     自反性。于任意的xx.equals(x)一定返回true

(2)     称性。于任意的xy,如果y.equals(x)返回truex.equals(y)也返回true

(3)     传递性。于任意的xyz,如果有x.equals(y)返回truey.equals(z)返回truex.equals(z)一定返回true

(4)     一致性。于任意的xy,如果象中用于等价比的信息没有改,那论调x.equals(y)多少次,返回的应该保持一致,要一直是true,要一直是false

(5)     任何不是nullxx.equals(null)一定返回false

equals()的是象的地址,如果要使用自己的HashMaplabel,必hashCode()equals()方法。

使用散列的目的:想要使用一个象来找另一个象。使用TreeSetTreeMap也能实现此目的。另外,可以自己实现一个Map,此,必提供Map.entrySet()方法来生成Map.Entry象的Set

使用散列的价:速度,散列使得查询可以快速行。散列将label保存中方便快速查询,因元素最快的数据构是数,用它来表示label的信息(后面有信息的描述),而不是label本身。通label算得到一个数字,作的下个数字就是散列(即前面所述的信息)散列具体是通在基Object中,可能由程序自定覆盖的hashCode()方法,即散列函数生成。了解决数容量来的限制,可以使不同的label生成相同的下,保存在一个list中,一个表就是数的一个元素。查询label就可以通过对list中的信息找,当散列函数比好,数个位置中的list短,可以快速找到数元素list中的某个位置,提高了整体速度。

散列表中的slot通常称bucket了使散列分均匀,bucket一般取数。但事实证明,实际上并不是散列bucket的理想容量,近来Java散列实现都使用2,具体如何验证以后再

3.HashMap的性能因子

容量(capacity):散列表中bucket的数量。

初始化容量(initial capacity):建散列表bucket的数量。可以在构造方法中指定HashMapHashSet的初始化容量。

尺寸(size):散列表中记录的数量。(的元素个数,非list中元素)

负载因子(load factor):尺寸/容量。负载因子0,表示空的散列表,0.5表示半的散列表。轻负载的散列表具有冲突少,适宜插入与查询的特点,但是使用迭代器遍会比慢。高的负载会减少所需空大小。当负载达到指定值时,容器会自成倍地增加容量,并将原有的象重新分配,存入新的bucket中,程称重散列

4.重写hashCode()关键

(1)     同一个hashCode()应该生成同

(2)     hashCode()方法不要依象中易的数据,当数据hashCode()就会生成一个不同的散列,即生了一个不同的label

(3)     hashCode()于具有唯一性的象信息,例如象地址。

(4)     散列码应该心速度,而不是唯一性,因散列不必是唯一的。

(5)     好的hashCode()应该产生分均匀的散列。在Effective Java(Addison-Wesley 2001)中,Joshua BlochhashCode()出了设计,可以参考。

写正确高效的hashCode()equals()可以参考ApacheJakarta Commons目中的工具。

java集合类总结

象的集合

如果程序的象数量有限,且寿命可知,那么这个程序是相当简单的。

与其它容器的区在三个方面:效率,识别以及可以持有primitives。数Java提供的,能随机存访问reference序列的多方法中的,最高效的一。数是一个简单线性序列,所有它可以快速的访问其中的元素。但是速度是有代价的;当你建了一个数之后,它的容量就固定了,而且在其生命周期里不能改。也你会提建一个数,等到快不用的候,再建一个新的,然后将旧的数里的reference全部到新的里面。其(我以后会的)ArrayList就是这么做的。但是这种灵活性所来的开销,使得ArrayList的效率比起数有了明下降。

Java和容器都做检查;如果了界,它旧会一个RuntimeException这种异常表明错误是由程序造成的,这样你就用不着再在程序里面检查了。

有一些泛型容器包括ListSetMap。他们处象的候就好像象都没有自己的具体型一。也就是,容器将它所含的元素都看成是(Java中所有的根Object的。这样你只需要建一容器,就能把所有型的象全都放去。从个角度来看,这种做法很不(只是苦了 primitive。如果是常量,你可以用JavaprimitiveWrapper;如果是量,那就只能放在你自己的里了)。与其他泛型容器相比,里体的第二革优势建数候,你也同指明了它所持有的象的型(又引出了第三点--数可以持有primitives,而容器却不行)。也就是,它会在编译候作检查,从而防止你插入错误类型的象,或者是在提取象的候把象的了。Java编译和运行都能阻止你将一个不恰当的消息传给对象。所有并不是使用容器就有什,只是如果编译器能帮你指定,那程序运行会更快,最也会少收到程序运行异常骚扰

从效率和检查的角度来看,使用数组总是没的。但是,如果你在解决一个更一般的问题,那数就会得功能太弱了点。

是第一流的

不管你用的是那种类型的数,数标识实际上都是一个建在堆(heap)里的实实在在的象的”reference实际上是那个象持有其他象的reference。你即可以用数的初始化句,含地象,也可以用new表达式,明确地象,只length属性能告你数能存多少元素。它是数组对象的一部分(实际上也是你唯一能访问的属性或方法)‘[]’法是另一条访问组对象的途径。

你没法知道数里面究竟放了多少元素,因length只是告你数能放多少元素,也就是是数组对象的容量,而不是它真正已持有的元素的数量。但是,建数组对象的候,它所持有的reference都会被自地初始化null,所以你可以通过检查的某个槽位是否null,来判断它是否持有象。以此推,primitive的数,会自来数字初始化零,字符初始化(char)0boolean初始化false

primitive容器

容器只能持有Object象的reference。而数除了能持有Objectsreference之外,可以直接持有primitive。当然可以使用IntegerDoublewrapper。把primitive放到容器中,淡这样总有点怪怪的。此外, primitive的效率要比wrapper容器的高出多。

当然,如果你使用primitive候,需要那能随需要自动扩展的容器的灵活性,那就不能用数了。你只能用容器来存primitivewrapper

返回一个数

你写了一个方法,它返回的不是一个而是一组东西。那Java中就可以返回的就是一个数。与C++不同,你永也不必Java的数操心--只要你需要它,它就在;一旦你用完了,垃圾回收器会帮你把它打

Arrays

java.util里面有一个Arrays,它包括了一可用于数static方法,些方法都是一些用工具。其中有四个基本方法:用来比两个数是否相等的equals();用来填充的fill();用来组进行排序的sort();以及用于在一个已排序的数找元素的 binarySearch()。所有些方法都primitiveObject行了重。此外有一个asList()方法,它接受一个数,然后把它成一个List容器。

Arrays是有用的,但它的功能并不完整。例来,如果它能不用写for就能直接打印数,那就好了。此外,正如你所看到的fill()只能用一个填数。所以,如果你想把随即生成的数字填fill()是无能力的。

制一个数

Java类库提供了一个System.arraycopy()static方法。相比for,它能以更快的速度拷System.arraycopy()所有型都作了重

象数primitive都能拷。但是如果你拷的是象数,那你只拷了它reference--象本身不会被拷被成浅拷shallow copy)。

的比

了能比是否完全相等,Arrays提供了equals()方法。当然,也是针对primitive以及Object的。两个数要想完全相等,他有相同数量的元素,而且数个元素必与另一个数的相对应的位置上的元素相等。元素的相等姓,用equals()判断。( primitive,它会使用其wrapperequals();比如int使用Integer.equals()。)。

元素的比

Java里面有两实现功能的方法。一是实现java.lang.Comparable接口,并以此实现类自有的方法。是一个很简单的接口,它只有一个方法compareTo()个方法能接受另一个象作参数,如果象比参数小,它就会返回一个数,如果相同返回零,如果有的象比参数大,它就返回一个正数。

static randInt()方法会生成一个介于0100的正数。

在架,有人你一个没有实现Comparable接口的,或者类实现Comparable接口,但是你发现它的工作方式不是你所希望的,于是要重新定一个新的比方法。Java没有求你一定要把进类里,它的解决方案是使用策略模式(strategy design pattern。有了策略之后,你就能把会的代封装到它自己的里(即所的策略strategy object)。你把策略象交不会的代,然后用它运用策略完成整个算法。这样,你就可以用不同的策略象来表示不同的比方法,然后把它都交同一个排序程序了。接下来就要过实现Comparator接口来定策略象了。个接口有两个方法compare()equals()。但是除非是有特殊的性能要求,否你用不着去实现equals()。因只要是,它就都含地承自Object,而Object里面已有了一个 equals()了。所以你尽可以使用缺省的Objectequals()这样就已经满足接口的要求了。

Collections专门有一个会返回与象自有的比法相反的Comparator的方法。它能很易地被用到CompType上面。

Collections.reverseOrder()返回了一个Comparatorreference

compare()方法会根据第一个参数是小于,等于是大于第二个参数,分返回整数,零或是正整数。

的排序

有了内置的排序方法之后,你就能任何数排序了,不primitive象数的,只要它实现Comparable接口或有一个与之相Comparator象就行了。

Java类库所用的排序算法已作了化--primitive,它用的是快速排序(Quicksort对对象,它用的是定合并排序(stable merge sort。所以除非是prolier表明排序算法是瓶,否你不用性能担心。

查询有序数

一旦数排完序,你就能Arrays.binarySearch()行快速查询了。但是切忌一个尚未排序的数使用binarySearch();因为这么做的果是没意的。

如果Arrays.binarySearch()找到了,它就返回一个大于或等于0。否它就返回一个负值,而负值要表达的意思是,如果你手动维护这个数值应该插在哪个止。就是:

-(插入点)-1

插入点就是,在所有比要找的那个更大中,最小的那个的下,或者,如果数中所有的都比要找的小,它就是a.size()

如果数里面有重元素,那它不能保会返回哪一个。个算法不支持重元素,不它也不报错。所以,如果你需要的是一个无重元素的有序序列的,那可以考使用本章后面所介TreeSet(支持【排序“sorted order”】)和LinkedHashSet(支持【插入“sorted order”】)。两个会帮你照看所有细节。只有在遇到性能瓶候,你才应该用手动维护的数来代替两个

如果排序的候用到了Comparator针对对象数primitive不允使用Comparator),那binarySearch()候,也必使用同一个Comparator(用个方法的重版)。

部分的总结

而言之,如果你要持有一组对象,首,同也是效率最高的选择应该是数。而且,如果是一primitive,你也只能用数有一些更一般的情况,也就是写程序的不知道要用多少象,或者要用一复杂方式来存储对象情况。此,Java提供了容器container class。其基本型有ListSetMap

们还有一些的特性。比方Set所持有的象,个个都不同,Map是一个关联性数associative array,它能在两个象之建立系。此外,与数不同,它们还能自动调整大小,所以你可以往里面放任意数量的象。

容器

Java2的重新设计1.01.1里面那个表的容器。新的设计凑也更合理。同它也补齐了容器类库的功能,提供了表(linked list),列(queue)和双向列(deques“decks”数据构的功能。

Java2的容器要解决持有,而它把问题分成两

1Collection:通常是一有一定律的独立元素。List按照特定的序持有些元素,而Set不能保存重的元素。(bag没有个限制,但是Java的容器类库没有实现它,因List提供这种功能了)

2Map:一--key-value)形式出pair。初看上去,它应该是一个pairCollection,但是真这么去做的,它就会得很滑稽,所以是把个概念独立列出来好。退一步说,真的要用到Map的某个自己的候,建一个Collection也是很方便的。 Map可以返回key)的”SetCollection,或者pairSet。和数Map不需要什修改,就能很容易地展成多。你只要直接把Map值设Map就可以了(然后它的再是Map,依此推)。

Java的容器分成两基本型。它的区就在,个位置能放多少象。Collection只允许每个位置上放一个象(个名字有点误导,因容器类库也常被collections)。它包括以一定序持有一组对List,以及只能允添加不重Set ArrayList是一List,而HashSet是一Set。你可以用add()方法往Collection里面加象。

Map保存的是key)--形式的pair,很像是一个微型数据

Map又被称为关联性数associative array)。你可以用put()方法往Map里面加元素。它接受--形式pair作参数。

fill()方法还为CollectionMap作了重出在默情况下使用容器toString()方法。打印出来的Collection会用方括号括起来,元素与元素之用逗号分Map会用花括号括起来,用等号起来(在左在右)。

List会老老实实地持有你所入的所有象,既不做排序也不做编辑Set则每象只接受一次,而且要用它自己的规则对元素行重新排序(一般情况下,你心的只是Set包没包括某个象,而不是它到底排在哪里--如果是那,你最好是用List)。而Map也不接收重pair,至于是不是重,要由key来决定。此外,它也有它自己的内部排序规则,不会受序影响。如果插入序是很重要的,那你就只能使用LinkedHashSet LinkedHashMap了。

填充容器

ArraysCollection也有一个叫Collections,它包含了一些静用工具方法,其中就有一个fill() fill()也只是把同一个象的reference制到整个容器,而且它只能List,不能SetMap工作。

容器的缺点:不知道象的

Java的容器有个缺点,就是往容器里面放象的候,会把象的型信息了。是因为开发容器的程序不会知道你要用它来保存什么类型的象,而容器只保存特定型的象又会影响它的通用性。所以容器被做成只有持有Object,也就是所有象的根reference这样它就能持有任何型的象了。(当然不包括primitive,因不是象,也没有象。)是一个很了不起的方案,只是:

1,由于在将象放入容器的候,它的型信息被扔掉了,所以容器能往里面加什么类型的没有限制。比方,即使你想它只持有cat人也能很易地把dog去。

2,由于象的型信息没了,容器只知道它持有的Objectreference,所以象在使用之前须进转换

好的一面是,Java不会用放容器里的象。假你往cat的容器里面扔了个dog,然后要把个容器里的所有象都当cat来用,当你把dogreferencecat的容器里面拉出来,并且试图将它转换cat候,就会引一个RuntimeException

ArrayList的用法也是很简单:先建一个,用add()象放去,要用的候再get()一个下--就跟用数差不多,只是不需要用方括号了。ArrayList也有一个size()方法,它会告你容器里面有多少象,这样你就不会粗心大意地了界然后引异常了。

即使不正确它也能运行

,即使不把转换成原先的型,它好像也能正常工作。有一情况比特殊:String能从编译器哪里得到一些能使之平工作的特殊帮助。只要编译器没能得到它所期望的String象,它就会toString()个方法油Object,能被任何Java覆写。它所返回的String象,会被用到任何要用它的地方。

于是只要覆写了toString()方法,你就能打印象了。

做一个型敏感的ArrayList

参数化型(Parameterized types

迭代器

是哪容器,你都得有法既能放西去,也能拿西出来。竟,容器的主要任就是存放象。ArrayListadd()就是用来放西的,而 get()是把象拿出来的法。ArrayList恨灵活;你可以随提取任何西,并且一个下上就能选择另一个元素。

迭代器(iterator又是一个设计模式)是一个象,它的任是,能在程序在不知道,或者不心他所理的是什么样的底序列的情况下,就能在一个象序列中前后移,并取其中的象。此外迭代器是一通常所象,既建代价很小的象。

JavaIterator就属于有这种限制的迭代器。它做不了很多事情,除了:

1,用iterator()方法叫容器传给你一个Iterator象。第一次Iteratornext()方法的候,它就会传给你序列中的第一个元素。

2,用next()方法取序列中的下一个象。

3,用hasNext()方法查询序列中是否有其他象。

4,用remove()方法除迭代器所返回的最后一个元素。

这么多了。只是迭代器的一个恨简单实现,不过还是很大(List有一个更精巧的ListIterator)。

意的递归Unintended recursion

由于Java准容器(同其它)也是Object的,因此它也有一个toString()方法。个方法已被覆写了,所以它能生成一个表示它自己以及所有它所保存的象的String。比如ArrayListtoString()方法就会遍ArrayList个元素,然后用它toString()方法。假你要打印的地址。好像最直接的法就是使用this。但是会出很多异常,解决的法就是去Object toString()方法,它就是干活的。所以不要用this应该super.toString()

容器分

根据程的需要,CollectionMap有好几个实现实际上只有三容器件--MapListSet,而每种又有两到三个实现

与存放象有的接口包Collection, List, SetMap。在理想情况下,大多数代码应该只同些接口打交道,只是在建容器的候才要精确地指明它的确切型。

add(),就像它的名字告的,会把新的元素放Collection。但是文档里面特地声明,“add()会确保容器包含指定的元素说给Set的,因它只添加原先没有的元素,ArrayList或其他Listadd()把它放,因List并不心它是不是保存了相同的元素。

Collection都能用iterator()方法生一个Iterator里,我Iterator来遍整个Collection,然后把他打印出来。

Collection的功能

下面这张出了Collection的所有功能,也就是你能用SetList做什事(不包括从Object动继来的方法)。(List有一些外的功能。)Map不是Collection的,所以我会区别对待。

boolean add(Object):确保容器能持有你传给它的那个参数。如果没有把它加去,就返回false。(是个的方法,本章稍后会再作解。)

boolean addAll(Collection):加入参数Collection所含的所有元素。只要加了元素,就返回true

void clear():清除容器所保存的所有元素。(

boolean contains(Object):如果容器持有参数Object,就返回true

boolean containsAll(Collection):如果容器持有参数Collection所含的全部元素,就返回true

boolean isEmpty():如果容器里面没有保存任何元素,就返回true

Iterator iterator():返回一个可以在容器的各元素之Iterator

boolean removeAll(Collection)除容器里面所有参数Collection所包含的元素。只要删过东西,就返回true。(

boolean retainAll(Collection):只保存参数Collection所包括的元素(集合交集的概念)。如果过变化,返回true。(

int size():返回容器所含元素的数量。

Object[] toArray():返回一个包含容器中所有元素的数

Object[] toArray(Object[] a):返回一个包含容器中所有元素的数,且个数不是普通的Object,它的应该同参数数a型相同(要做转换)。

注意,里没有能行随机访问get()方法。是因Collection包括Set。而Set有它自己的内部序(因此随即访问事毫无意的)。所以如果你要检查Collection的元素,你就必使用迭代器。

接下来List, SetMap的各种实现了,每讲容器,我都会(用星号)告你默情况下应该选用哪种实现

List的功能

List的基本用法事相当将但的。大多数候,你只是用add()象,用get()象,用iterator()个序列的Iterator,但List有一些的很有用的方法。

实际上有两List:擅长对元素行随机访问的,常用的ArrayList,和更大的LinkedListLinkedList不是快速的随机访问设计的,但是它却有一更加通用的方法。

Lisk(接口):List的最重要的特征就是有序;它会确保以一定的序保存元素。ListCollection的基上添加了大量方法,使之能在序列中插入和除元素。(只LinkedList推荐使用。)List可以制造ListIterator象,你除了能用它在List的中插入和除元素之外,能用它沿两个方法遍List

ArrayList*:一个用数组实现List。能行快速的随机访问,但是往列表中插入和除元素的候比慢。ListIterator只能用在反向遍ArrayList合,不要用它来插入和除元素,因相比LinkedList,在ArrayList里面用ListIterator的系统开销高。

LinkedList对顺访问进行了化。在List插入和除元素的代价也不高。随机访问的速度相对较慢。(用ArrayList吧。)此外它addFirst()addLast()getFirst()getLast()removeFirst()removeLast()等方法(些方法,接口和基均未定),你能把它当成stack),列(queue)或双向列(deque)来用。

住,容器只是一个存储对象的盒子。如果个笑盒子能帮你解决所有的问题,那你就用不着取管它事怎么实现的(在大多数情况下,是使用象的基本概念)。如果开发环境里面有一些的,会造成固定的性能开销的因素存在,那ArrayListLinkedList的性能差就会得不那重要了。你只需要它中的一个,你甚至可以想象有这样完美的抽象容器;它能根据用途,自地切其底实现

LinkedList做一个

stack也被称先出LIFO)的容器。就是,最后一个被进栈中的西,会第一个出来。同其他Java容器一压进去和出来的西都是Object,所以除非你只用Object的功能,否就必须对弹起来的西转换

LinkedList的方法能直接实现栈的功能,所以你完全可以不写Stack而直接使用LinkedList

如果你只想要的功能,那么继承就不太合适了,因为继承出来的是一个LinkedList的所有方法的

LinkedList做一个

列(queue)是一个先出FIFO)容器。也就是,你把一端把西放去,从另一端把西取出来。所以你放西的序也就是取西的序。LinkedList有支持列的功能的方法,所以它也能被当作Queue来用。

能很易地用LinkedList做一个deque(双向列)。它很像列,只是你可以从任意一端添加和除元素。

Set的功能

Set的接口就是Collection的,所以不像那两个List,它没有外的功能。实际Set确确实实就是一个Collection--只不方式不同了。(承和多性的完美运用:表达不同地行。)Set会拒持有多个具有相同象的例(象的又是由什决定的呢?问题较复杂,我以后会)。

Set(接口):加入Set个元素必是唯一的;否Set是不会把它加去的。要想加SetObjectequals()这样才能象的唯一性。Set的接口和Collection的一摸一Set的接口不保它会用哪种顺序来存元素。

HashSet*为优查询速度而设计Set。要放HashSet里面的Object得定hashCode()

TreeSet:是一个有序的Set,其底是一颗树这样你就能从Set里面提取一个有序序列了。

LinkedHashSet(JDK 1.4):一个在内部使用表的Set,既有HashSet查询速度,又能保存元素被加去的序(插入序)。用IteratorSet候,它是按插入访问的。

HashSet保存象的序是和TreeSetLinkedHashSet不一的。是因是用不同的方法来存找元素的。(TreeSet用了一的数据构【red-black tree data structure】来元素排序,而HashSet用了专为快速找而设计的散列函数。LinkedHashSet在内部用散列来提高查询速度,但是它看上去像是用表来保存元素的插入序的。)你写自己的候,一定要住,Set要有一个判断以什么顺序来存元素的准,也就是你必须实现 Comparable接口,并且定compareTo()方法。

SortedSet

SortedSet(只有TreeSet一个实现可用)中的元素一定是有序的。使得SortedSet接口多了一些方法:

Comparator comparator():返回Set使用的Comparator象,或者用null表示它使用Object自有的排序方法。

Object first():返回最小的元素。

Object last():返回最大的元素。

SortedSet subSet(fromElement,toElement):返回Set的子集,其中的元素从fromElement始到toElement止(包括fromElement,不包括toElement)。

SortedSet headSet(toElement):返回Set的子集,其中的元素都小于toElement

SortedSet headSet(toElement):返回Set的子集,其中的元素都大于fromElement

注意,SortedSet意思是根据象的比较顺,而不是插入行排序。

Map的功能

ArrayList你用数字在一愕嘎象序列里面选择,所以从某,它是将数字和关联起来。但是,如果你想根据其他条件在一个象序列里面选择,那又做呢?就是一个例子。它的准是取最后一个被。我常用的术语mapdictionary,或 associative array就是一非常大的,能在序列里面行挑的工具。从概念上,它看上去像是一个ArrayList,但它不用数字,而是用另一个象来象!是一重要的程技巧。

一概念在Java中表现为Mapput(Object key, Object value)方法会往Map里面加一个,并且把(你所用的象)系起来。之后,get(Object key)就会返回与之相。你也可以用containsKey()containsValue()测试Map是否包含有某个

Java类库里有好几MapHashMapTreeMapLinkedHashMapWeakHashMap,以及 IdentityHashMap。它实现Map的基本接口,但是在行方式方面有着明异。些差异体在,效率,持有和表示pair序,持有象的时间长短,以及如何决定的相等性。

性能Map所要面的一个大问题。如果你知道get()工作的,你就会发觉(比方)在ArrayList里面找象会是相当慢的。而正是 HashMap强项。它不是慢慢地一个个地找,而是用了一被称hashcode的特殊找的。散列(hash算法,它会从目标对象当中提取一些信息,然后生成一个表示象的独特int hashCode()Object的方法,因此所有Java象都能生成hash codeHashMap利用象的hashCode()行快速的找。这样性能就有了急的提高。

Map(接口):--系(既pairs),这样就能用来找

HashMap*:基于hash表的实现。(用它来代替Hashtable。)提供时间恒定的插入与查询。在构造函数可以hash表的capacityload factor。可以通构造函数来调节其性能。

LinkedHashMap(JDK 1.4):很像HashMap,但是用Iterator行遍候,它会按插入序或最先使用的序(least-recently-used (LRU)order访问。除了用Iterator外,其他情况下,只是比HashMap稍慢一点。用Iterator的情况下,由于是使用表来保存内部序,因此速度会更快。

TreeMap:基于数据构的实现。当你pair,会发现们时序(根据ComparableComparator,我们过一会)排列的。TreeMap的特点,你得到的一个有序的MapTreeMapMap中唯一有subMap()方法的实现个方法能中的一部分。

WeakHashMap:一个weak keyMap,是某些特殊问题设计的。它能Map放其所持有的象。如果某个象除了在Map当中充当之外,在其他地方都没有其reference,那它将被当作垃圾回收。

IdentityHashMap(JDK 1.4):一个用==,而不是equals()来比较键hash map。不是平常使用而设计的,是用来解决特殊问题的。

散列是往Map里存数据的常用算法。

SortedMap

SortedMap(只有TreeMap一个实现)的肯定是有序的,因此个接口里面就有一些附加功能的方法了。

Comparator comparator():返回Map所使用的comparator,如果是用Object内置的方法的返回null

Object firstKey():返回第一个

Object lastKey():返回最后一个

SortedMap subMap(fromKey, toKey):返回Map的一个子集,其fromKey始到toKey止,包括前者,不包括后者。

SortedMap headMap(toKey):返回Map的一愕嘎子集,其均小于toKey

SortedMap tailMap(fromKey):返回Map的一个子集,其均大于等于fromKey

pair是按key序存的,由于TreeMap序的概念,因此位置是有意的,所以你可以去取它的第一个和最后一个元素,以及它的子集。

LinkedHashMap

了提高速度,LinkedHashMap所有西都做了hash,而且遍候(println()会遍整个Map,所以你能看到程)会按插入序返回pair。此外,你可以在LinkedHashMap的构造函数里面行配置它使用基于访问LRUleast-recently -used)算法,这样还没被访问过的元素(同也是要除的候选对象)就会出列的最前这样为节源而写一个定清理的程序就得很简单了。

散列算法与Hash

一个合适的equals()做到一下五点:

1 反身性:任何x, x.equals(x)true的。

2 称性:任何xy,如果y.equals(x)true的,那

x.equals(y)也必true的。

3 传递性:任何xyz,如果x.equals(y)true的,且

y.equals(z)也是true的,那x.equals(z)也必true的。

4 一致性:任何xy,如果象里面用来判断相等姓的信息没有修

,那论调用多少次x.equals(y),它都必一致地返回

truefalse

5 于任何非空的xx.equals(null)返回false

Object.equals()只是简单地比两个象的地址。

如果你想把子集写的HashMap来用的,你就必hashCode()equals()都覆写了。

理解hashCode()

如果你不覆写hashCode()equals(),散列数据构(HashSetHashMapLinkedHashSet,或LinkedHashMap)就没法正确地

散列的价就在于速度:散列算法能很快地找出西。

是最快的数据构。

持有reference

java.lang.ref类库里有一套能增垃圾回收器工作的灵活性的。一旦碰到了象达到要耗光内存候,就会的格外有用。有三个承抽象Reference的:SoftReferenceWeakReferencePhantomReference。如果待理的象只能通过这Reference访问,那么这Reference象就会向垃圾回收器提供一些不同级别的暗事。

……

总结Java类库的容器

1。数象和数字形式的下标联系起来。它持有的是型确定的象,这样提取象的候就不用再作传递了。它可以是多的,也可以持有primitive。但是建之后它的容量不能改了。

2Collection持有个元素,而Map持有相关联pair

3。和数List也把数字下系起来,你可以把数List想成有序的容器。List会随元素的增加自动调整容量。但是List只能持有Object reference,所以不能存放primitive,而且把Object提取出来之后,要做传递

4。如果要做很多随机访问,那么请ArrayList,但是如果要再List的中做很多插入和除的,就应该LinkedList了。

5LinkedList能提供列,双向列和的功能。

6Map提供的不是象与数关联,而是象和象的关联

HashMap看重的是访问速度,而TreeMap各国那看重序,因而它不如HashMap快。而LinkedHashMap保持象插入的序,但是也可以用LRU算法它重新排序。

7Set只接受不重象。HashSet提供了最快的查询速度。而TreeSet保持元素有序。LinkedHashSet保持元素的插入序。

8。没必要再在新代里使用旧类库留下来的VectorHashtableStack了。

容器类库是你天都会用到的工具,它能使程序更介,更大并且更搞笑。

最近在一本J2EE中看到了很不集合框架的明文章,筛选上来和大家共享,集合框架提供管理象集合的接口和.它包含接口,,算法,以下是它的各个件的.

Collection接口

Collection是最基本的集合接口,一个Collection代表一Object,即Collection的元素(Elements)。一些Collection相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接承自CollectionJava SDK提供的都是承自Collection子接口ListSet

所有实现Collection接口的都必提供两个准的构造函数:无参数的构造函数用于建一个空的Collection,有一个Collection参数的构造函数用于建一个新的Collection个新的Collection入的Collection有相同的元素。后一个构造函数允户复制一个Collection

如何遍Collection中的一个元素?不Collection实际类型如何,它都支持一个iterator()的方法,方法返回一个迭代子,使用迭代子即可逐一访问Collection一个元素。典型的用法如下:

Iterator it =collection.iterator(); //得一个迭代子

while(it.hasNext()) {

Object obj = it.next(); // 得到下一个元素

}

Collection接口派生的两个接口是ListSet

List接口

List是有序的Collection,使用此接口能精确的控制个元素插入的位置。用使用索引(元素在List中的位置,似于数)来访问List中的元素,这类似于Java的数

和下面要提到的Set不同,List有相同的元素。

除了具有Collection接口必iterator()方法外,List提供一个listIterator()方法,返回一个ListIterator接口,和准的Iterator接口相比,ListIterator多了一些add()的方法,允添加,除,定元素,能向前或向后遍

实现List接口的常用LinkedListArrayListVectorStack

LinkedList

LinkedList实现List接口,允null元素。此外LinkedList提供外的getremoveinsert方法在LinkedList的首部或尾部。些操作使LinkedList可被用作堆stack),列(queue)或双向列(deque)。

注意LinkedList没有同方法。如果多个线程同时访问一个List自己实现访问。一解决方法是在List构造一个同List

List list =Collections.synchronizedList(new LinkedList(…));

ArrayList

ArrayList实现了可大小的数。它允所有元素,包括nullArrayList没有同

sizeisEmptygetset方法运行时间为常数。但是add方法开销为的常数,添加n个元素需要O(n)时间。其他的方法运行时间为线性。

ArrayList例都有一个容量(Capacity),即用于存元素的数的大小。个容量可随着不断添加新元素而自增加,但是增算法并没有定。当需要插入大量元素,在插入前可以ensureCapacity方法来增加ArrayList的容量以提高插入效率。

LinkedListArrayList也是非同的(unsynchronized)。

Vector

Vector非常ArrayList,但是Vector是同的。由Vector建的Iterator然和ArrayList建的Iterator是同一接口,但是,因Vector是同的,当一个Iterator建而且正在被使用,另一个线程改Vector的状(例如,添加或除了一些元素),这时调Iterator的方法将抛出ConcurrentModificationException,因此必获该异常。

Stack

Stack承自Vector实现一个后先出的堆Stack提供5外的方法使得Vector得以被当作堆使用。基本的pushpop方法,peek方法得到栈顶的元素,empty方法测试是否空,search方法检测一个元素在堆中的位置。Stack刚创建后是空

Set接口

Set是一不包含重的元素的Collection,即任意的两个元素e1e2都有e1.equals(e2)=falseSet最多有一个null元素。

很明Set的构造函数有一个束条件,入的Collection参数不能包含重的元素。

注意:必小心操作可变对象(Mutable Object)。如果一个Set中的可元素改了自身状态导Object.equals(Object)=true致一些问题

Map接口

注意,Map没有Collection接口,Map提供keyvalue的映射。一个Map中不能包含相同的keykey只能映射一个valueMap接口提供3集合的视图Map的内容可以被当作一key集合,一value集合,或者一key-value映射。

Hashtable

HashtableMap接口,实现一个key-value映射的哈希表。任何非空(non-null)的象都可作key或者value

添加数据使用put(key, value),取出数据使用get(key)两个基本操作的时间开销为常数。

Hashtableinitialcapacityload factor两个参数整性能。通常缺省的load factor 0.75好地实现时间和空的均衡。增大load factor可以省空但相时间将增大,会影响像getput这样的操作。

使用Hashtable简单示例如下,将123放到Hashtable中,他key”one””two””three”

Hashtable numbers = newHashtable();

numbers.put(“one”, newInteger(1));

numbers.put(“two”, newInteger(2));

numbers.put(“three”, newInteger(3));

要取出一个数,比如2,用相key

Integer n =(Integer)numbers.get(“two”);

System.out.println(“two = ” +n);

由于作key象将通过计算其散列函数来确定与之对应value的位置,因此任何作key象都必须实现hashCodeequals方法。hashCodeequals方法承自根Object,如果你用自定当作key,要相当小心,按照散列函数的定,如果两个象相同,即obj1.equals(obj2)=truehashCode相同,但如果两个象不同,hashCode不一定不同,如果两个不同象的hashCode相同,这种现象称冲突,冲突会致操作哈希表的时间开销增大,所以尽量定好的hashCode()方法,能加快哈希表的操作。

如果相同的象有不同的hashCode哈希表的操作会出意想不到的果(期待的get方法返回null),要避免这种问题,只需要牢一条:要同时复equals方法和hashCode方法,而不要只写其中一个。

Hashtable是同的。

HashMap

HashMapHashtable似,不同之在于HashMap是非同的,并且允null,即null valuenull key。,但是将HashMap视为Collectionvalues()方法可返回Collection),其迭代子操作时间开销HashMap的容量成比例。因此,如果迭代操作的性能相当重要的,不要将HashMap的初始化容量高,或者load factor低。

WeakHashMap

WeakHashMap是一HashMap,它key弱引用,如果一个key不再被外部所引用,那么该key可以被GC回收。

总结

如果及到堆列等操作,应该List于需要快速插入,除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList

如果程序在单线境中,或者访问仅仅在一个线程中行,考非同,其效率高,如果多个线程可能同操作一个应该使用同

要特注意哈希表的操作,作key象要正确equalshashCode方法。

尽量返回接口而非实际型,如返回List而非ArrayList这样如果以后需要将ArrayListLinkedList,客端代不用改就是针对抽象程。

Java言内置的型,除此之外,Java有多保存象引用的方式。Java类库提供了一套相当完整的容器,使用的方法可以保存和操纵对象。下面分别进讨论,在研究Java容器之前,先了解一下Java的基本功能和特性
1.  
的基本特
与其它种类的容器(List/Set/Map)的区在于效率、确定的型和保存基本型数据的能力。数是一高效的存和随机访问对象引用序列的方式,使用数可以快速的访问中的元素。但是当建一个数组对(注意和象数的区)后,数的大小也就固定了,当数不足的候就再建一个新的数,把旧的数中所有的引用制到新的数
Java
中的数和容器都需要检查,如果越界就会得到一个RuntimeException异常。点和C++中有所不同,C++vector的操作符[]不会做检查在速度上会有一定的提高,Java的数和容器会因为时刻存在的检查带来一些性能上的开销
Java
中通用的容器不会以具体的型来象,容器中的象都是以Object理的,Java中所有的基。另外,数可以保存基本型,而容器不能,它只能保存任意的Java
一般情况下,考到效率与检查应该尽可能考使用数。如果要解决一般化的问题,数可能会受到一些限制,这时可以使用Java提供的容器
2.  
操作数用功
java.util.Arrays中,有static方法,提供了操作数的一些基本功能
equals()
方法—-用于比两个数是否相等,相等的条件是两个数的元素个数必相等,并且对应位置的元素也相等
fill()
方法—-用以某个填充整个数个方法有点笨
asList()
方法—-接受任意的数组为参数,将其转变为List容器。
binarySearch()
方法—-用于在已排序的数找元素,需要注意的是必是已排序的数。当Arrays.binarySearch()找到了找目标时方法将返回一个等于或大于0,否将返回一个负值,表示在目前的排序状下此目元素所应该插入的位置。负值算公式是“-x-1”x指的是第一个大于象的元素在数中的位置,如果数中所有的元素都小于要找的象,x = a.size()。如果数中包含重的元素,无法保找到的是哪一个元素,如果需要没有重元素的数排序,可以使用TreeSet或者LinkedHashSet。另外,如果使用Comparator排序了某个象数,在使用方法提供同Comparator型的参数。需要注意的是,基本型数无法使用Comparator行排序
sort()
方法—-组进行升序排序
Java类库中,另有static方法System.arraycopy()用来制数,它针对所有型做了重

3.  的排
Java1.01.1两个版本中,类库缺少基本的算法操作,包括排序的操作,Java2行了改善。在行排序的操作,需要根据象的实际类行比操作,如果为每种不同的型各自写一个不同的排序方法,将会使得代用。一般的程序设计标应将保持不的事物与会的事物相分离。在里,不的是通用的排序算法,化的是各种对象相互比的方式
Java
有两方式来实现的功能,一实现java.lang.Comparable接口,接口只有一个compareTo()方法,并以一个Object类为参数,如果当前象小于参数返回负值,如果相等返回零,如果当前象大于参数返回正。另一方法是采用策略(strategy)设计模式,将会化的代封装在它自己的(策略)中,再将策略象交保持不的代中,后者使用此策略实现它的算法。因此,可以不同的比方式生成不同的象,将它用在同的排序程序中。在此情况下,通一个实现Comparator接口的建了一个策略,个策略compare()equals()两个方法,一般情况下实现compare()方法即可。
使用上述两方法即可任意基本型的数组进行排序,也可以任意的象数组进行排序。再提示一遍,基本型数无法使用Comparator行排序
Java
类库中的排序算法针对排序的行了——针对基本设计快速排序针对对设计并排序。一般不用担心其性能。

Java容器分析–ListSet
容器可以大大提高程效率和程能力,在Java2中,所有的容器都由SUN公司的Joshua Bloch行了重新设计,丰富了容器类库的功能
Java2
容器类类库的用途是保存,它分
Collection—-
独立的元素,通常些元素都服从某种规则List保持元素特定的序,而Set不能有重元素
Map—-
键值对象,即其元素是成象,最典型的用就是数据字典,并且有其它广泛的用。另外,Map可以返回其所有键组成的Set和其所有值组成的Collection,或其键值对组成的Set,并且可以像数样扩展多Map,只要Map键值对是一个Map即可。
1.
迭代器
迭代器是一种设计模式,它是一个象,它可以遍选择序列中的象,而开发不需要了解序列的底层结构。迭代器通常被称象,因为创建它的代价小
Java
中的Iterator功能比较简单,并且只能向移
(1)    
使用方法iterator()要求容器返回一个Iterator。第一次Iteratornext()方法,它返回序列的第一个元素
(2)    
使用next()得序列中的下一个元素
(3)    
使用hasNext()检查序列中是否有元素
(4)    
使用remove()将迭代器新返回的元素
Iterator
Java迭代器最简单实现List设计ListIterator具有更多的功能,它可以从两个方向遍List,也可以从List中插入和除元素
2.List
的功能方法
List(interface):
次序是List最重要的特点;它确保维护元素特定的序。ListCollection添加了多方法,使得能List插入与移除元素(只推荐LinkedList使用)。一个List可以生成ListIterator,使用它可以从两个方向遍List,也可以从List插入和除元素
ArrayList:
由数组实现List。它允许对元素行快速随机访问,但是向List插入与移除元素的速度很慢。ListIterator应该来由后向前遍ArrayList,而不是用来插入和除元素,因为这LinkedList开销要大很多
LinkedList:
对顺访问进行了化,向List插入与除得开销不大,随机访问则对较(可用ArrayList代替)。它具有方法addFirst()addLast()getFirst()getLast()removeFirst()removeLast()些方法(没有在任何接口或基中定义过)使得LinkedList可以当作堆列和双向列使用
3.Set
的功能方法
Set(interface):
存入Set个元素必是唯一的,因Set不保存重元素。加入SetObjectequals()方法以确保象的唯一性。SetCollection有完全一的接口。Set接口不保证维护元素的次序
HashSet:
快速找而设计Set。存入HashSet象必hashCode()
TreeSet:
保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。
LinkedHashSet:
具有HashSet查询速度,且内部使用维护元素的(插入的次序)。于是在使用迭代器遍Set果会按元素插入的次序
HashSet
采用散列函数元素行排序,专门为快速查询设计的;TreeSet采用的数据行排序元素;LinkedHashSet内部使用散列以加快查询速度,同使用维护元素的次序,使得看起来元素是以插入的序保存的。需要注意的是,生成自己的类时Set需要维护元素的存储顺序,因此要实现Comparable接口并定compareTo()方法。

Java容器分析–Map

准的Java类库中包含了几种类型的Map,它有同的基本接口Map,但是行特性各不相同,主要表在效率、键值对的保存、元素呈次序、象的保存周期和判定是否等价的策略等方面
1.Map
的功能方法
Map(interface):
维护labelvalue关联性,使得可以通labelvalue
HashMap: Map
基于散列表的实现,取代了Hashtable。插入和查询label/value开销是固定的,并且可以通构造器置容量和负载因子,以整容器的性能
LinkedHashMap:
HashMap的基上做了一些,在迭代遍,取得label/value序是其插入的次序,或者是最近最少使用(LRU)的次序,速度上比HashMap要慢一点,但在迭代访问时速度会更快,主要原因是它使用了维护内部次序
TreeMap:
labellabel/value,元素会被排序,其次序由ComparableComparator决定,因此查询所得到的果是经过排序的。另外,它是唯一subMap()方法的Map具体,即返回一个子。它也是SortedMap接口的唯一实现subMap()方法也是从接口的。
WeakHashMap: Weak Key
映射,允许释放映射所指向的象。当映射之外没有引用指向某个label,此label可以被垃圾收集器回收。
IdentityHashMap:
使用==代替equals()label行比的散列映射
2.hashCode()
当使用中的IntegerHashMaplabel,程序能正常运行,但是使用自己建的HashMaplabel,通常犯一个错误
HashMap中通labelvalue实际上是label象地址的散列来确定value的。一般情况下,我是使用基Object的方法hashCode()来生成散列,它默是使用象的地址来算的,因此由第一个new Apple(5)和第二个new Apple(5)生成的散列是不同的,不能完成正确的找。通常,我可以写自己的hashCode()方法来覆盖基的原始方法,但与此同,我时实现equals()方法来判断当前的label是否与表中存在的label相同。正确的equals()方法足五个条件
(1)    
自反性。于任意的xx.equals(x)一定返回true
(2)    
称性。于任意的xy,如果y.equals(x)返回truex.equals(y)也返回true
(3)    
传递性。于任意的xyz,如果有x.equals(y)返回truey.equals(z)返回truex.equals(z)一定返回true
(4)    
一致性。于任意的xy,如果象中用于等价比的信息没有改,那论调x.equals(y)多少次,返回的应该保持一致,要一直是true,要一直是false
(5)    
任何不是nullxx.equals(null)一定返回false
equals()
的是象的地址,如果要使用自己的HashMaplabel,必hashCode()equals()方法。
使用散列的目的:想要使用一个象来找另一个象。使用TreeSetTreeMap也能实现此目的。另外,可以自己实现一个Map,此,必提供Map.entrySet()方法来生成Map.Entry象的Set
使用散列的价:速度,散列使得查询可以快速行。散列将label保存中方便快速查询,因元素最快的数据构是数,用它来表示label的信息(后面有信息的描述),而不是label本身。通label算得到一个数字,作的下个数字就是散列(即前面所述的信息)散列具体是通在基Object中,可能由程序自定覆盖的hashCode()方法,即散列函数生成。了解决数容量来的限制,可以使不同的label生成相同的下,保存在一个list中,一个表就是数的一个元素。查询label就可以通过对list中的信息找,当散列函数比好,数个位置中的list短,可以快速找到数元素list中的某个位置,提高了整体速度。
散列表中的slot通常称bucket了使散列分均匀,bucket一般取数。但事实证明,实际上并不是散列bucket的理想容量,近来Java散列实现都使用2,具体如何验证以后再
3.HashMap
的性能因子
容量(capacity):散列表中bucket的数量。
初始化容量(initial capacity):建散列表bucket的数量。可以在构造方法中指定HashMapHashSet的初始化容量。
尺寸(size): 散列表中记录的数量。(的元素个数,非list中元素)
负载因子(load factor):尺寸/容量。负载因子0,表示空的散列表,0.5表示半的散列表。轻负载的散列表具有冲突少,适宜插入与查询的特点,但是使用迭代器遍会比慢。高的负载会减少所需空大小。当负载达到指定值时,容器会自成倍地增加容量,并将原有的象重新分配,存入新的bucket中,程称重散列
4.
重写hashCode()关键
(1)    
同一个hashCode()应该生成同
(2)     hashCode()
方法不要依象中易的数据,当数据hashCode()就会生成一个不同的散列,即生了一个不同的label
(3)     hashCode()
于具有唯一性的象信息,例如象地址
(4)    
散列码应该心速度,而不是唯一性,因散列不必是唯一的
(5)    
好的hashCode()应该产生分均匀的散列。在Effective Java(Addison-Wesley 2001)中,Joshua BlochhashCode()出了设计,可以参考
写正确高效的hashCode()equals()可以参考ApacheJakarta Commons目中的工具。

java集合类总结 象的集合 如果程序的象数量有限,且寿命可知,那么这个程序是相当简单的。数与其它容器的区在三个方面:效率,识别以及可以持有primitives。数Java提供的,能随机存访问reference序列的多方法中的,最高效的一。数是一个简单线性序列,所有它可以快速的访问其中的元素。但是速度是有代价的;当你建了一个数之后,它的容量就固定了,而且在其生命周期里不能改。也你会提建一个数,等到快不用的候,再建一个新的,然后将旧的数里的reference全部到新的里面。其(我以后会的)ArrayList就是这么做的。但是这种灵活性所来的开销,使得ArrayList的效率比起数有了明下降。Java和容器都做检查;如果了界,它旧会一个RuntimeException这种异常表明错误是由程序造成的,这样你就用不着再在程序里面检查了。有一些泛型容器包括ListSetMap。他们处象的候就好像象都没有自己的具体型一。也就是,容器将它所含的元素都看成是(Java中所有的根Object的。这样你只需要建一容器,就能把所有型的象全都放去。从个角度来看,这种做法很不(只是苦了primitive。如果是常量,你可以用JavaprimitiveWrapper;如果是量,那就只能放在你自己的里了)。与其他泛型容器相比,里体的第二革优势建数候,你也指明了它所持有的象的型(又引出了第三点--数可以持有primitives,而容器却不行)。也就是,它会在编译候作检查,从而防止你插入错误类型的象,或者是在提取象的候把象的了。Java编译和运行都能阻止你将一个不恰当的消息传给对象。所有并不是使用容器就有什,只是如果编译器能帮你指定,那程序运行会更快,最也会少收到程序运行异常的骚扰。从效率和检查的角度来看,使用数组总是没的。但是,如果你在解决一个更一般的问题,那数就会得功能太弱了点。是第一流的 不管你用的是那种类型的数,数标识实际上都是一个建在堆(heap)里的实实在在的象的”reference实际上是那个象持有其他象的reference。你即可以用数的初始化句,含地象,也可以用new表达式,明确地象,只length属性能告你数能存多少元素。它是数组对象的一部分(实际上也是你唯一能访问的属性或方法)。‘[]’法是另一条访问组对象的途径。你没法知道数里面究竟放了多少元素,因length只是告你数能放多少元素,也就是数组对象的容量,而不是它真正已持有的元素的数量。但是,建数组对象的候,它所持有的reference都会被自地初始化null,所以你可以通过检查的某个槽位是否null,来判断它是否持有象。以此推,primitive的数,会自来数字初始化零,字符初始化(char)0boolean初始化falseprimitive容器容器只能持有Object象的reference。而数除了能持有Objectsreference之外,可以直接持有primitive。当然可以使用IntegerDoublewrapper。把primitive放到容器中,淡这样总有点怪怪的。此外,primitive的效率要比wrapper容器的高出多。当然,如果你使用primitive候,需要那能随需要自动扩展的容器的灵活性,那就不能用数了。你只能用容器来存primitivewrapper。返回一个数你写了一个方法,它返回的不是一个而是一组东西。那Java中就可以返回的就是一个数。与C++不同,你永也不必Java的数操心--只要你需要它,它就在;一旦你用完了,垃圾回收器会帮你把它打Arraysjava.util里面有一个Arrays,它包括了一可用于数static方法,些方法都是一些用工具。其中有四个基本方法:用来比两个数是否相等的equals();用来填充的fill();用来组进行排序的sort();以及用于在一个已排序的数找元素的binarySearch()。所有些方法都primitiveObject行了重。此外有一个asList()方法,它接受一个数,然后把它成一个List容器。Arrays是有用的,但它的功能并不完整。例来,如果它能不用写for就能直接打印数,那就好了。此外,正如你所看到的fill()只能用一个填数。所以,如果你想把随即生成的数字填fill()是无能力的。制一个数Java类库提供了一个System.arraycopy()static方法。相比for,它能以更快的速度拷System.arraycopy()所有型都作了重象数primitive都能拷。但是如果你拷的是象数你只拷了它reference--象本身不会被拷被成浅拷shallowcopy)。的比较为了能比是否完全相等,Arrays提供了equals()方法。当然,也是针对primitive以及Object的。两个数要想完全相等,他有相同数量的元素,而且数个元素必与另一个数的相对应的位置上的元素相等。元素的相等姓,用equals()判断。( primitive,它会使用其wrapperequals();比如int使用Integer.equals()。)。数元素的比Java里面有两实现功能的方法。一是实现java.lang.Comparable接口,并以此实现类自有的方法。是一个很简单的接口,它只有一个方法compareTo()个方法能接受另一个象作参数,如果象比参数小,它就会返回一个数,如果相同返回零,如果有的象比参数大,它就返回一个正数。static randInt()方法会生成一个介于0100的正数。在架,有人你一个没有实现Comparable接口的,或者类实现Comparable接口,但是你发现它的工作方式不是你所希望的,于是要重新定一个新的比方法。Java没有求你一定要把比进类里,它的解决方案是使用策略模式(strategy design pattern。有了策略之后,你就能把会的代封装到它自己的里(即所的策略strategy object)。你把策略象交不会的代,然后用它运用策略完成整个算法。这样,你就可以用不同的策略象来表示不同的比方法,然后把它都交同一个排序程序了。接下来就要过实现Comparator接口来定策略象了。个接口有两个方法compare()equals()。但是除非是有特殊的性能要求,否你用不着去实现equals()。因只要是,它就都含地承自Object,而Object里面已有了一个 equals()了。所以你尽可以使用缺省的Objectequals()这样就已经满足接口的要求了。Collections专门有一个会返回与象自有的比法相反的Comparator的方法。它能很易地被用到CompType上面。Collections.reverseOrder()返回了一个Comparatorreferencecompare()方法会根据第一个参数是小于,等于是大于第二个参数,分返回整数,零或是正整数。数的排序 有了内置的排序方法之后,你就能任何数排序了,不primitive象数的,只要它实现Comparable接口或有一个与之相Comparator象就行了。Java类库所用的排序算法已作了化--primitive,它用的是快速排序(Quicksort对对象,它用的是定合并排序(stable merge sort。所以除非是prolier表明排序算法是瓶,否你不用性能担心。查询有序数 一旦数排完序,你就能用Arrays.binarySearch()行快速查询了。但是切忌一个尚未排序的数使用binarySearch();因为这么做的果是没意的。如果Arrays.binarySearch()找到了,它就返回一个大于或等于0。否它就返回一个负值,而负值要表达的意思是,如果你手动维护这个数值应该插在哪个止。就是:-(插入点)-1“插入点就是,在所有比要找的那个更大中,最小的那个的下,或者,如果数中所有的都比要找的小,它就是a.size()。如果数里面有重元素,那它不能保会返回哪一个。个算法不支持重元素,不它也不报错。所以,如果你需要的是一个无重元素的有序序列的,那可以考使用本章后面所介TreeSet(支持【排序“sorted order”】)和LinkedHashSet(支持【插入“sorted order”】)。两个会帮你照看所有细节。只有在遇到性能瓶候,你才应该用手动维护的数来代替两个。如果排序的候用到了Comparator针对对象数primitive不允使用Comparator),那binarySearch()候,也必使用同一个Comparator(用个方法的重版)。数部分的总结 而言之,如果你要持有一组对象,首,同也是效率最高的选择应该是数。而且,如果是一primitive,你也只能用数有一些更一般的情况,也就是写程序的不知道要用多少象,或者要用一复杂方式来存储对象情况。此,Java提供了容器container class。其基本ListSetMap。它们还有一些的特性。比方Set所持有的象,个个都不同,Map是一个关联性数associative array,它能在两个象之建立系。此外,与数不同,它们还能自动调整大小,所以你可以往里面放任意数量的象。容器Java2的重新设计1.01.1里面那个表的容器。新的设计凑也更合理。同它也补齐了容器类库的功能,提供了表(linked list),列(queue)和双向列(deques“decks”数据构的功能。Java2的容器要解决持有,而它把问题分成两1Collection:通常是一有一定律的独立元素。List按照特定的序持有些元素,而Set不能保存重的元素。(bag没有个限制,但是Java的容器类库没有实现它,因List提供这种功能了)2Map:一--key-value)形式出pair。初看上去,它应该是一个pairCollection,但是真这么去做的,它就会得很滑稽,所以是把个概念独立列出来好。退一步说,真的要用到Map的某个自己的候,建一个Collection也是很方便的。 Map可以返回key)的”SetCollection,或者pairSet。和数Map不需要什修改,就能很容易地展成多。你只要直接把Map值设Map就可以了(然后它的再是Map,依此推)。Java的容器分成两基本型。它的区就在,个位置能放多少象。Collection只允许每个位置上放一个象(个名字有点误导,因容器类库也常被collections)。它包括以一定序持有一组对List,以及只能允添加不重Set ArrayList是一List,而HashSet是一Set。你可以用add()方法往Collection里面加象。Map保存的是key)--形式的pair,很像是一个微型数据Map又被称为关联性数associative array)。你可以用put()方法往Map里面加元素。它接受--形式pair作参数。fill()方法还为CollectionMap作了重出在默情况下使用容器toString()方法。打印出来的Collection会用方括号括起来,元素与元素之用逗号分Map会用花括号括起来,用等号起来(在左在右)。List会老老实实地持有你所入的所有象,既不做排序也不做编辑Set则每象只接受一次,而且要用它自己的规则对元素行重新排序(一般情况下,你心的只是Set包没包括某个象,而不是它到底排在哪里--如果是那,你最好是用List)。而Map也不接收重pair,至于是不是重,要由key来决定。此外,它也有它自己的内部排序规则,不会受序影响。如果插入序是很重要的,那你就只能使用LinkedHashSet LinkedHashMap了。填充容器和ArraysCollection也有一个叫Collections,它包含了一些静用工具方法,其中就有一个fill() fill()也只是把同一个象的reference制到整个容器,而且它只能List,不能SetMap工作。容器的缺点:不知道象的Java的容器有个缺点,就是往容器里面放象的候,会把象的型信息了。是因为开发容器的程序不会知道你要用它来保存什么类型的象,而容器只保存特定型的象又会影响它的通用性。所以容器被做成只有持有Object,也就是所有象的根reference这样它就能持有任何型的象了。(当然不包括primitive,因不是象,也没有象。)是一个很了不起的方案,只是:1,由于在将象放入容器的候,它的型信息被扔掉了,所以容器能往里面加什么类型的没有限制。比方,即使你想它只持有cat人也能很易地把dog去。2,由于象的型信息没了,容器只知道它持有的Objectreference,所以象在使用之前须进转换。好的一面是,Java不会用放容器里的象。假你往cat的容器里面扔了个dog,然后要把个容器里的所有象都当cat来用,当你把dogreferencecat的容器里面拉出来,并且试图将它转换cat候,就会引一个RuntimeExceptionArrayList的用法也是很简单:先建一个,用add()象放去,要用的候再get()一个下--就跟用数差不多,只是不需要用方括号了。ArrayList也有一个size()方法,它会告你容器里面有多少象,这样你就不会粗心大意地了界然后引异常了。有即使不正确它也能运行有,即使不把转换成原先的型,它好像也能正常工作。有一情况比特殊:String能从编译器哪里得到一些能使之平工作的特殊帮助。只要编译器没能得到它所期望的String象,它就会toString()个方法油Object,能被任何Java覆写。它所返回的String象,会被用到任何要用它的地方。于是只要覆写了toString()方法,你就能打印象了。做一个型敏感的ArrayList参数化Parameterized types)迭代器无是哪容器,你都得有法既能放西去,也能拿西出来。竟,容器的主要任就是存放象。ArrayListadd()就是用来放西的,而 get()是把象拿出来的法。ArrayList恨灵活;你可以随提取任何西,并且一个下上就能选择另一个元素。迭代器(iterator又是一个设计模式)是一个象,它的任是,能在程序在不知道,或者不心他所理的是什么样的底序列的情况下,就能在一个象序列中前后移,并取其中的象。此外迭代器是一通常所象,既建代价很小的象。JavaIterator就属于有这种限制的迭代器。它做不了很多事情,除了:1,用iterator()方法叫容器传给你一个Iterator象。第一次Iteratornext()方法的候,它就会传给你序列中的第一个元素。2,用next()方法取序列中的下一个象。3,用hasNext()方法查询序列中是否有其他象。4,用remove()方法除迭代器所返回的最后一个元素。就这么多了。只是迭代器的一个恨简单实现,不过还是很大(List有一个更精巧的ListIterator)。不意的递归Unintended recursion)由于Java准容器(同其它)也是Object的,因此它也有一个toString()方法。个方法已被覆写了,所以它能生成一个表示它自己以及所有它所保存的象的String。比如ArrayListtoString()方法就会遍ArrayList个元素,然后用它toString()方法。假你要打印的地址。好像最直接的法就是使用this。但是会出多异常,解决的法就是去Object toString()方法,它就是干活的。所以不要用this应该super.toString()。容器分学根据程的需要,CollectionMap有好几个实现实际上只有三容器件--MapListSet,而每种又有两到三个实现。与存放象有的接口包括Collection, List, SetMap。在理想情况下,大多数代码应该只同些接口打交道,只是在建容器的候才要精确地指明它的确切型。add(),就像它的名字告的,会把新的元素放Collection。但是文档里面特地声明,“add()会确保容器包含指定的元素说给Set的,因它只添加原先没有的元素,ArrayList或其他Listadd()把它放,因List并不心它是不是保存了相同的元素。Collection都能用iterator()方法生一个Iterator里,我Iterator来遍整个Collection,然后把他打印出来。Collection的功能下面这张出了Collection的所有功能,也就是你能用SetList做什事(不包括从Object动继来的方法)。(List有一些外的功能。)Map不是Collection的,所以我会区别对待。boolean add(Object):确保容器能持有你传给它的那个参数。如果没有把它加去,就返回false。(是个的方法,本章稍后会再作解。)boolean addAll(Collection):加入参数Collection所含的所有元素。只要加了元素,就返回truevoid clear():清除容器所保存的所有元素。(boolean contains(Object):如果容器持有参数Object,就返回trueboolean containsAll(Collection):如果容器持有参数Collection所含的全部元素,就返回trueboolean isEmpty():如果容器里面没有保存任何元素,就返回trueIterator iterator():返回一个可以在容器的各元素之Iteratorboolean removeAll(Collection)除容器里面所有参数Collection所包含的元素。只要删过东西,就返回true。(boolean retainAll(Collection):只保存参数Collection所包括的元素(集合交集的概念)。如果过变化,返回true。(int size():返回容器所含元素的数量。Object[] toArray():返回一个包含容器中所有元素的数Object[] toArray(Object[] a):返回一个包含容器中所有元素的数,且个数不是普通的Object,它的应该同参数数a型相同(要做转换)。注意里没有能行随机访问get()方法。是因Collection包括Set。而Set有它自己的内部序(因此随即访问事毫无意的)。所以如果你要检查Collection的元素,你就必使用迭代器。接下来List,SetMap的各种实现了,每讲容器,我都会(用星号)告你默情况下应该选用哪种实现List的功能List的基本用法事相当将但的。大多数候,你只是用add()象,用get()象,用iterator()个序列的Iterator,但List有一些的很有用的方法。实际上有两List:擅长对元素行随机访问的,常用的ArrayList,和更大的LinkedListLinkedList不是快速的随机访问设计的,但是它却有一更加通用的方法。Lisk(接口):List的最重要的特征就是有序;它会确保以一定的序保存元素。ListCollection的基上添加了大量方法,使之能在序列中插入和除元素。(只LinkedList推荐使用。)List可以制造ListIterator象,你除了能用它在List的中插入和除元素之外,能用它沿两个方法遍ListArrayList*:一个用数组实现List。能行快速的随机访问,但是往列表中插入和除元素的候比慢。ListIterator只能用在反向遍ArrayList合,不要用它来插入和除元素,因相比LinkedList,在ArrayList里面用ListIterator的系统开销高。LinkedList对顺访问进行了化。在List插入和除元素的代价也不高。随机访问的速度相对较慢。(用ArrayList吧。)此外它addFirst()addLast()getFirst()getLast()removeFirst()removeLast()等方法(些方法,接口和基均未定),你能把它当成stack),列(queue)或双向列(deque)来用。住,容器只是一个存储对象的盒子。如果个笑盒子能帮你解决所有的问题,那你就用不着取管它事怎么实现的(在大多数情况下,是使用象的基本概念)。如果开发环境里面有一些的,会造成固定的性能开销的因素存在,那ArrayListLinkedList的性能差就会得不那重要了。你只需要它中的一个,你甚至可以想象有这样完美的抽象容器;它能根据用途,自地切其底实现。用LinkedList做一个stack也被称先出LIFO)的容器。就是,最后一个被进栈中的西,会第一个出来。同其他Java容器一压进去和出来的西都是Object,所以除非你只用Object的功能,否就必须对弹起来的西转换LinkedList的方法能直接实现栈的功能,所以你完全可以不写Stack而直接使用LinkedList。如果你只想要的功能,那么继承就不太合适了,因为继承出来的是一个LinkedList的所有方法的。用LinkedList做一个列(queue)是一个先出FIFO)容器。也就是,你把一端把西放去,从另一端把西取出来。所以你放西的序也就是取西的序。LinkedList有支持列的功能的方法,所以它也能被当作Queue来用。能很易地用LinkedList做一个deque(双向列)。它很像列,只是你可以从任意一端添加和除元素。Set的功能Set的接口就是Collection的,所以不像那两个List,它没有外的功能。实际Set确确实实就是一个Collection--只不方式不同了。(承和多性的完美运用:表达不同地行。)Set会拒持有多个具有相同象的例(象的又是由什决定的呢?问题较复杂,我以后会)。Set(接口):加入Set个元素必是唯一的;否Set是不会把它加去的。要想加SetObjectequals()这样才能象的唯一性。Set的接口和Collection的一摸一Set的接口不保它会用哪种顺序来存元素。HashSet*为优查询速度而设计Set。要放HashSet里面的Object得定hashCode()TreeSet:是一个有序的Set,其底是一颗树这样你就能从Set里面提取一个有序序列了。LinkedHashSet(JDK 1.4):一个在内部使用表的Set,既有HashSet查询速度,又能保存元素被加去的序(插入序)。用IteratorSet候,它是按插入访问的。HashSet保存象的序是和TreeSetLinkedHashSet不一的。是因是用不同的方法来存找元素的。(TreeSet用了一的数据构【red-black tree data structure】来元素排序,而HashSet用了专为快速找而设计的散列函数。LinkedHashSet在内部用散列来提高查询速度,但是它看上去像是用表来保存元素的插入序的。)你写自己的候,一定要住,Set要有一个判断以什么顺序来存元素的准,也就是你必须实现Comparable接口,并且定compareTo()方法。SortedSetSortedSet(只有TreeSet一个实现可用)中的元素一定是有序的。使得SortedSet接口多了一些方法:Comparator comparator():返回Set使用的Comparator象,或者用null表示它使用Object自有的排序方法。Object first():返回最小的元素。Object last():返回最大的元素。SortedSet subSet(fromElement, toElement):返回Set的子集,其中的元素从fromElement始到toElement止(包括fromElement,不包括toElement)。SortedSet headSet(toElement):返回Set的子集,其中的元素都小于toElementSortedSet headSet(toElement):返回Set的子集,其中的元素都大于fromElement。注意,SortedSet意思是根据象的比较顺,而不是插入行排序。Map的功能ArrayList你用数字在一愕嘎象序列里面选择,所以从某,它是将数字和关联起来。但是,如果你想根据其他条件在一个象序列里面选择那又做呢?就是一个例子。它的准是取最后一个被。我常用的术语mapdictionary,或 associative array就是一非常大的,能在序列里面行挑的工具。从概念上,它看上去像是一个ArrayList,但它不用数字,而是用另一个象来象!是一重要的程技巧。一概念在Java中表现为Mapput(Object key, Object value)方法会往Map里面加一个,并且把(你所用的象)系起来。之后,get(Object key)就会返回与之相。你也可以用containsKey()containsValue()测试Map是否包含有某个Java类库里有好几MapHashMapTreeMapLinkedHashMapWeakHashMap,以及 IdentityHashMap。它实现Map的基本接口,但是在行方式方面有着明异。些差异体在,效率,持有和表示pair序,持有象的时间长短,以及如何决定的相等性。性能Map所要面的一个大问题。如果你知道get()工作的,你就会发觉(比方)在ArrayList里面找象会是相当慢的。而正是 HashMap强项。它不是慢慢地一个个地找,而是用了一被称hashcode的特殊找的。散列(hash算法,它会从目标对象当中提取一些信息,然后生成一个表示象的独特int hashCode()Object的方法,因此所有Java象都能生成hash codeHashMap利用象的hashCode()行快速的找。这样性能就有了急的提高。Map(接口):--系(既pairs),这样就能用来找了。HashMap*:基于hash表的实现。(用它来代替Hashtable。)提供时间恒定的插入与查询。在构造函数可以hash表的capacityload factor。可以通构造函数来调节其性能。LinkedHashMap(JDK 1.4):很像HashMap,但是用Iterator行遍候,它会按插入序或最先使用的序(least-recently-used (LRU)order访问。除了用Iterator外,其他情况下,只是比HashMap稍慢一点。用Iterator的情况下,由于是使用表来保存内部序,因此速度会更快。TreeMap:基于数据构的实现。当你pair,会发现们时序(根据ComparableComparator,我们过一会)排列的。TreeMap的特点,你得到的一个有序的MapTreeMapMap中唯一有subMap()方法的实现个方法能中的一部分。WeakHashMap:一个weak keyMap,是某些特殊问题设计的。它能Map放其所持有的象。如果某个象除了在Map当中充当之外,在其他地方都没有其reference,那它将被当作垃圾回收。IdentityHashMap(JDK 1.4):一个用==,而不是equals()来比较键hash map。不是平常使用而设计的,是用来解决特殊问题的。散列是往Map里存数据的常用算法。SortedMap SortedMap(只有TreeMap一个实现)的肯定是有序的,因此个接口里面就有一些附加功能的方法了。Comparator comparator():返回Map所使用的comparator,如果是用Object内置的方法的返回nullObject firstKey():返回第一个Object lastKey():返回最后一个SortedMap subMap(fromKey, toKey):返回Map的一个子集,其fromKey始到toKey止,包括前者,不包括后者。SortedMap headMap(toKey):返回Map的一愕嘎子集,其均小于toKeySortedMap tailMap(fromKey):返回Map的一个子集,其均大于等于fromKeypair是按key序存的,由于TreeMap序的概念,因此位置是有意的,所以你可以去取它的第一个和最后一个元素,以及它的子集。LinkedHashMap了提高速度,LinkedHashMap所有西都做了hash,而且遍候(println()会遍整个Map,所以你能看到程)会按插入序返回pair。此外,你可以在LinkedHashMap的构造函数里面行配置,它使用基于访问LRUleast-recently -used)算法,这样还没被访问过的元素(同也是要除的候选对象)就会出列的最前这样为节源而写一个定清理的程序就得很简单了。散列算法与Hash数一个合适的equals()做到一下五点:1反身性:任何x, x.equals(x)true的。2称性:任何xy,如果y.equals(x)true的,那x.equals(y)也必true的。3传递性:任何xyz,如果x.equals(y)true的,且y.equals(z)也是true的,那x.equals(z)也必true的。4一致性:任何xy,如果象里面用来判断相等姓的信息没有修改,那论调用多少次x.equals(y),它都必一致地返回truefalse5于任何非空的xx.equals(null)返回false。默Object.equals()只是简单地比两个象的地址。如果你想把子集写的HashMap来用的,你就必hashCode()equals()都覆写了。理解hashCode()如果你不覆写hashCode()equals(),散列数据构(HashSetHashMapLinkedHashSet,或LinkedHashMap)就没法正确地。散列的价就在于速度:散列算法能很快地找出西。数是最快的数据构。持有reference java.lang.ref类库里有一套能增垃圾回收器工作的灵活性的。一旦碰到了象达到要耗光内存候,就会的格外有用。有三个承抽象Reference的:SoftReferenceWeakReferencePhantomReference。如果待理的象只能通过这Reference访问,那么这Reference象就会向垃圾回收器提供一些不同级别的暗事。……
总结Java类库的容器 1。数象和数字形式的下标联系起来。它持有的是型确定的象,这样提取象的候就不用再作传递了。它可以是多的,也可以持有primitive。但是建之后它的容量不能改了。2Collection持有个元素,而Map持有相关联pair3。和数List也把数字下系起来,你可以把数List想成有序的容器。List会随元素的增加自动调整容量。但是List只能持有Object reference,所以不能存放primitive,而且把Object提取出来之后,要做传递4。如果要做很多随机访问,那么请ArrayList,但是如果要再List的中做很多插入和除的,就应该LinkedList了。5LinkedList能提供列,双向列和的功能。6Map提供的不是象与数关联,而是象和象的关联HashMap看重的是访问速度,而TreeMap各国那看重序,因而它不如HashMap快。而LinkedHashMap保持象插入的序,但是也可以用LRU算法它重新排序。7Set只接受不重象。HashSet提供了最快的查询速度。而TreeSet保持元素有序。LinkedHashSet保持元素的插入序。8。没必要再在新代里使用旧类库留下来的VectorHashtableStack了。容器类库是你天都会用到的工具,它能使程序更介,更大并且更搞笑。最近在一本J2EE中看到了很不集合框架的明文章,筛选上来和大家共享,集合框架提供管理象集合的接口和.它包含接口,,算法,以下是它的各个件的. Collection接口   Collection是最基本的集合接口,一个Collection代表一Object,即Collection的元素(Elements)。一些Collection相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接承自CollectionJava SDK提供的都是承自Collection子接口ListSet   所有实现Collection接口的都必提供两个准的构造函数:无参数的构造函数用于建一个空的Collection,有一个Collection参数的构造函数用于建一个新的Collection个新的Collection入的Collection有相同的元素。后一个构造函数允户复制一个Collection   如何遍Collection中的一个元素?不Collection实际类型如何,它都支持一个iterator()的方法,方法返回一个迭代子,使用迭代子即可逐一访问Collection一个元素。典型的用法如下:     Iterator it =collection.iterator(); // 得一个迭代子     while(it.hasNext()) {       Object obj = it.next(); // 得到下一个元素       由Collection接口派生的两个接口是ListSet
List
接口   List是有序的Collection,使用此接口能精确的控制个元素插入的位置。用使用索引(元素在List中的位置,似于数)来访问List中的元素,这类似于Java的数 和下面要提到的Set不同,List有相同的元素。   除了具有Collection接口必iterator()方法外,List提供一个listIterator()方法,返回一个ListIterator接口,和准的Iterator接口相比,ListIterator多了一些add()的方法,允添加,除,定元素,能向前或向后遍   实现List接口的常用LinkedListArrayListVectorStack
LinkedList
   LinkedList实现List接口,允null元素。此外LinkedList提供外的getremoveinsert方法在LinkedList的首部或尾部。些操作使LinkedList可被用作堆stack),列(queue)或双向列(deque)。   注意LinkedList没有同方法。如果多个线程同时访问一个List自己实现访问。一解决方法是在List构造一个同List     List list = Collections.synchronizedList(newLinkedList(…));
ArrayList
   ArrayList实现了可大小的数。它允所有元素,包括nullArrayList没有同 sizeisEmptygetset方法运行时间为常数。但是add方法开销为的常数,添加n个元素需要O(n)时间。其他的方法运行时间为线性。   ArrayList例都有一个容量(Capacity),即用于存元素的数的大小。个容量可随着不断添加新元素而自增加,但是增算法并没有定。当需要插入大量元素,在插入前可以ensureCapacity方法来增加ArrayList的容量以提高插入效率。   和LinkedListArrayList也是非同的(unsynchronized)。
Vector
   Vector非常ArrayList,但是Vector是同的。由Vector建的Iterator然和ArrayList建的Iterator是同一接口,但是,因Vector是同的,当一个Iterator建而且正在被使用,另一个线程改Vector的状(例如,添加或除了一些元素),这时调Iterator的方法将抛出ConcurrentModificationException,因此必获该异常
Stack
   Stack承自Vector实现一个后先出的堆Stack提供5外的方法使得Vector得以被当作堆使用。基本的pushpop方法,peek方法得到栈顶的元素,empty方法测试是否空,search方法检测一个元素在堆中的位置。Stack刚创建后是空
Set
接口   Set是一不包含重的元素的Collection,即任意的两个元素e1e2都有e1.equals(e2)=falseSet最多有一个null元素。   很明Set的构造函数有一个束条件,入的Collection参数不能包含重的元素。   注意:必小心操作可变对象(Mutable Object)。如果一个Set中的可元素改了自身状态导Object.equals(Object)=true致一些问题
Map
接口   注意,Map没有Collection接口,Map提供keyvalue的映射。一个Map中不能包含相同的keykey只能映射一个valueMap接口提供3集合的视图Map的内容可以被当作一key集合,一value集合,或者一key-value映射。
Hashtable
   HashtableMap接口,实现一个key-value映射的哈希表。任何非空(non-null)的象都可作key或者value   添加数据使用put(key, value),取出数据使用get(key)两个基本操作的时间开销为常数。 Hashtableinitial capacityload factor两个参数整性能。通常缺省的load factor 0.75好地实现时间和空的均衡。增大load factor可以省空但相时间将增大,会影响像getput这样的操作。 使用Hashtable简单示例如下,将123放到Hashtable中,他key”one””two””three”     Hashtable numbers = newHashtable();     numbers.put(“one”, new Integer(1));     numbers.put(“two”, new Integer(2));     numbers.put(“three”, new Integer(3));   要取出一个数,比如2,用相key     Integer n = (Integer)numbers.get(“two”);     System.out.println(“two = ” + n);   由于作key象将通过计算其散列函数来确定与之对应value的位置,因此任何作key象都必须实现hashCodeequals方法。hashCodeequals方法承自根Object,如果你用自定当作key,要相当小心,按照散列函数的定,如果两个象相同,即obj1.equals(obj2)=truehashCode相同,但如果两个象不同,hashCode不一定不同,如果两个不同象的hashCode相同,这种现象称冲突,冲突会致操作哈希表的时间开销增大,所以尽量定好的hashCode()方法,能加快哈希表的操作。   如果相同的象有不同的hashCode哈希表的操作会出意想不到的果(期待的get方法返回null),要避免这种问题,只需要牢一条:要同时复equals方法和hashCode方法,而不要只写其中一个。   Hashtable是同
HashMap
   HashMapHashtable似,不同之在于HashMap是非同的,并且允null,即null valuenull key。,但是将HashMap视为Collectionvalues()方法可返回Collection),其迭代子操作时间开销HashMap的容量成比例。因此,如果迭代操作的性能相当重要的,不要将HashMap的初始化容量高,或者load factor
WeakHashMap
   WeakHashMap是一HashMap,它key弱引用,如果一个key不再被外部所引用,那么该key可以被GC回收。
总结   如果及到堆列等操作,应该List于需要快速插入,除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList   如果程序在单线境中,或者访问仅仅在一个线程中行,考非同,其效率高,如果多个线程可能同操作一个应该使用同   要特注意哈希表的操作,作key象要正确equalshashCode方法。   尽量返回接口而非实际型,如返回List而非ArrayList这样如果以后需要将ArrayListLinkedList,客端代不用改就是针对抽象