第11章 持有对象

Set对于每个值都只保存一个对象,Map是允许你将某些对象与其他一些对象关联起来的关联数组,Java容器类都可以自动地调整自己的尺寸。
如果一个类没有显示地声明继承自哪个类,那么它自动地继承自Object。
Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:
1)Collection。一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
2)Map。一组成对的“键值对”对象。允许你使用键来查找值。
Arrays.asList()方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数),并将其转换为一个List。Collection.addAll()方法接受一个Collection对象,以及一个数组或是一个用逗号分割的列表,将元素添加到Collection中。Collection的构造器可以接受另一个Collection,用它来将自身初始化。
有两种类型的List:
1)基本的ArrayList,它长于随机访问元素,但是再List的中间插入和移除元素时较慢。
2)LinkedList,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问。LinkedList在随机访问方面相对比较慢,但是它的特性集较ArrayList更大。
List允许在它被创建之后添加元素、移除元素,或者自我调整尺寸。contains()方法来确定某个对象是否在列表中。remove()方法可以移除一个对象。使用indexOf()发现对象在List中所处位置的索引编号。subList()允许你从较大的列表中创建出一个片断。containsAll()方法来判断片断是否在列表中,retainAll()方法使一种有效的“交集”操作,removeAll()方法将从List中移除在参数List中的所有元素。set()是在指定的索引处(第一个参数),用第二个参数替换整个位置的元素。addAll()方法使得我们可以再初始List的中间插入新的列表。isEmpty()用来判断List列表中有没有元素。clear()方法用来清除List中元素。通过toArray()方法,将任意的Collection转换为一个数组。
迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构。Java的Iterator只能单向移动,这个Iterator只能用来:
1)使用方法iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
2)使用next()获得序列中的下一个元素。
3)使用hasNext()检查序列中是否还有元素。
4)使用remove()将迭代器新近返回的元素删除。
ListIterator是一个更加强大的Iterator的子类型,它只能用于各种List类的访问。ListIterator可以双向移动。它还可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引,并且可以使用set()方法替换它访问过的最后一个元素。可以通过调用listIterator()方法产生一个指向List开始处的ListIterator,并且还可以通过调用listIterator(n)方法创建一个一开始就指向列表索引为n的元素处的ListIterator。
LinkedList也实现了基本的List接口,还添加了可以使其用作栈、队列或双端队列的方法,getFirst()和element()返回列表的头(第一个元素),而不移除它,如果List为空,抛异常,peek()方法与之相同,只是在List为空时返回null。removeFirst()与remove()相同,移除并返回列表的头,列表为空抛异常,poll()只是在列表为空时返回null。addFirst()与add()和addlast()相同,他们都是将某个元素插入到列表的尾(端)部。removeLast()移除并返回列表的最后一个元素。
“栈”通常是指“后进先出”(LIFO)的容器。LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。
Set不保存重复的元素。如果你试图将相同对象的多个实例添加到Set中,那么它就会阻止这种重复现象。Set具有与Collection完全一样的接口,因此没有任何额外的功能。TreeSet将元素存储在红-黑树数据结构中,而HashSet使用的是散列函数。LinkedHashSet使用了链表来维护元素的插入顺序。
在Map中,如果键不在容器中,get()方法将返回null,否则,get()方法将产生与该键相关联的value的值。通过containsKey()和containsValue()方法测试一个Map中,是否包含某个键或某个值。Map与数组和其他的Collection一样,可以很容易地扩展到多维。Map可以返回它的键的Set,它的值的Collection,或者它的键值对的Set。keySet()方法产生所有键组成的Set。values()方法返回此映射中包含的值的Collection。entrySet()方法返回此映射中包含的映射关系的Set。
Queue队列是一个典型的先进先出(FIFO)的容器。即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列常被当作一种可靠的将对象从程序的某个区域传输到另一个区域的途径。
LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现。offer()在允许的情况下,将一个元素插入到队尾,或者返回false。peek()和element()都将在不移除的情况下返回队头,但是peek()方法在队列为空时返回null,而element()会抛出异常。poll()和remove()方法将移除并返回队头,但是poll()在队列为空时返回null,remove()抛出异常。
先进先出描述了最典型的队列规则。队列规则是指在给定一组队列中的元素的情况下,确定下一个弹出队列的元素的规则。先进先出声明的是下一个元素应该是等待时间最长的元素。优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)。
在PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序。默认的排序将使用对象在队列中的自然顺序,但是可以通过提供自己的Comparator来修改这个顺序。Collections.reverseOrder()产生反序的Comparator。
Collection是描述所有序列容器的共性的根接口,它可能会被认为是一个“附属接口”,即因为要表示其他若干个接口的共性而出现的接口。Collection是Iterable类型。
生成Iterator是将队列与消费队列的方法连接在一起耦合度最小的方式,并且与实现Collection相比,它在序列类上所施加的约束也少得多。
尝试把一个数组当作一个Iterable参数传递会导致失败。这说明不存在任何从数组到Iterable的自动转换,必须手工执行这种转换。
Arrays.asList()产生的List对象会使用底层数组作为其物理实现是很重要的。只要执行的操作会修改这个List,并且不想原来的数组被修改,就应该在另一个容器中创建一个副本。
Java提供了大量持有对象的方式:
1)数组将数字与对象联系起来。保存类型明确对象,也可以是多维的。一旦生成,容量不能改变。
2)Collection保存单一的元素,而Map保存相关联的键值对。容器不能持有基本类型,通过自动包装会双向转换。
3)List也建立了数字索引与对象的关联。
4)进行大量随机访问使用ArrayList,经常从表中间插入或删除元素使用LinkedList。
5)各种Queue以及栈的行为,由LinkedList提供支持。
6)Map是一种将对象(而非数字)与对象相关联的设计。HashMap用来快速访问,TreeMap保持“键”始终处于排序状态,LinkedHashMap保持元素插入的顺序。
7)Set不接受重复元素。HashSet提供最快的查询速度,TreeSet保持元素处于排序状态。LinkedHashSet以插入顺序保存元素。
8)新程序不应该使用过时的Vector、Hashtable和Stack。
简单的容器分类 点线框表示接口,实线框表示普通的(具体的)类。带有空心箭头的点线表示一个特定的类实现了一个接口,实心箭头表示某个类可以生产箭头所指向类的对象。 除了TreeSet之外的所有Set都拥有与Collection完全一样的接口。List和Collection存在着明显的不同,尽管List所要求的方法都在Collection中。另一方面,在Queue接口中的方法都是独立的;在创建具有Queue功能的实现时,不需要使用Collection方法。最后,Map和Collection之间唯一的重叠就是Map可以使用entrySet()和values()方法来产生Collection。