如果一个程序只包含固定数量的且其生命期都是已知的对象,那么这是一个非常简单的程序。
基本的容器(集合类)List、Set、Queue和Map。
Set:每个值都只保存一个对象。
Map:允许将某些对象与其他一些对象关联起来的数组。
本章将了解有关Java容器类库的基本知识,以及对典型用法的重点介绍。
11.1 泛型和类型安全的容器
@SuppressWarnings注解及其参数表示只有有关“不受检查的异常”的警告信息应该被抑制。
一个类没有显示地声明继承自哪个类,那么它自动地继承自Object。
ArrayList
11.2 基本概念
Java容器类库的用途是“保存对象”,并将其划分为两个不同的概念:
1)Collection:一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)
2)Map:一组成对的“键值对”对象,允许你使用键来查找值。ArrayList允许你使用数字来查找值,因此在某种意义上讲,它将数字与对象关联在了一起。映射表允许我们使用另一个对象来查找某个对象,它也被称为“关联数组”,因为它将某些对象与另外一些对象关联在了一起,或者被称为“字典”,因为你可以使用键对象来查找值对象,就像在字典中使用单词来定义一样。Map是强大的编程工具。
List
ArrayList已经被向上转型为List。
如果你决定去修改你的实现,只需要在创建出修改它
List
Collection接口概括了序列的概念:一种存放一组对象的方式。
11.3 添加一组元素
Arrays.asList()方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数),并将其转型为一个List对象。
Collections.addAll()方法接受一个Collection对象,以及一个数组或是一个用逗号分割的列表,将元素添加到Collection中。
Collections.addAll()运行快。只能接受另一个Collection对象作为参数。
Arrays.asList()、Collections.addAll()灵活。这两个方法使用的都是可变参数列表。
Arrays.asList()中插入一条“线索”,告诉编译器对于由Arrays.asList()产生的List类型,实际的目标类型是什么,这称为显式类型参数说明。
11.4 容器的打印
Collection和Map的区别:容器中每个“槽”保存的元素个数。
Collection在每个槽中只能保存一个元素。此类容器包括:List,它以特定的顺序保存一组元素;Set,元素不能重复;Queue,只允许在容器的一“端”插入对象,并从另一“端“移除对象。
Map在每个槽内保存了两个对象,即键和与之相关联的值。
ArrayList和LinkedList都是List类型,LinkedList包含的操作也多于ArrayList。
HashSet、TreeSet和LinkedHashSet都是Set类型,HashSet使用的是相当复杂的方式存储元素,但也是最快获取元素的方式;TreeSet按照比较结果的升序保存对象,LinkedHashSet按照被添加的顺序保存对象。
Map(也被称为关联数组)使得你可以用键来查找对象,键所关联的对象称为值。对于每个键,Map只接受存储一次。Map.put(key,value)方法将增加一个值,并将它与某个键关联起来;Map.get(key)方法将产生与这个键相关联的值。
HashMap、TreeMap和LinkedHashMap,HashMap提供了最快的查找技术,没有按照任何明显的顺序来保存其元素;TreeMap按照比较结果的升序保存键;LinkedHashMap则按照插入顺序保存键,同时还保留了HashMap的查询速度。
11.5 List
List可以将元素维护在特定的序列中,List接口在Collection的基础上添加了大量的方法,使得可以在List的中间插入和移除元素。
ArrayList,它长于随机访问元素,但是在List的中间插入和移除元素时较慢。
LinkedList,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问,LinedList在随机访问方面相对比较们,但是它的特性集较ArrayList更大。
静态的Pet.arrayList()方法将返回一个填充了随机选取的Pet对象的ArrayList.
11.6 迭代器
迭代器:遍历并选择序列中的对象,轻量级对象(创建它的代价小)(一种设计模式)。
例如Iterator:
1)使用方法iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
2)使用next()获得序列中的下一个元素。
3)使用hasNext()检查序列中是否还有元素。
4)使用remove()将迭代器新折返回的元素删除。
Iterator能将遍历序列的操作与序列底层的结构分离。
11.7 LinkedList
LinkedList与ArrayList一样实现了基本的List接口,但是它执行了某些操作(在List的中间插入和移除)时比ArrayList更高效,但是在随机访问操作方面却差点。
getFirst()、element():返回列表的头(第一个元素),而并不移除它,如果List为空,则抛出NoSuchElementException.
peek():返回列表的头(第一个元素),而并不移除它,如果List为空则返回null。
removeFirst()、remove():移除并返回列表的头,而在列表为空时抛出NoSuchElementException.
poll():移除并返回列表头,而在列表为空时抛出null
addFirst()、add()、addLast():将某个元素插入到列表的尾(端)部。
removeLast()移除并返回列表的最后一个元素。
11.8 stack
“栈”通常是指“后进先出”(LIFO)的容器,有时栈也被称为叠加栈。
最后“压入”栈的元素,第一个“弹出”栈。
LinkedList可以实现栈的所有功能的方法。
11.9 Set
Set不保存重复的元素。常见的操作时使用contains()测试Set的归属性。查找的实现:HashSet专门对快速查找进行了优化。HashSet使用了散列插入。TreeSet将元素存储在红-黑树数据结构中,而HashSet使用的是散列函数。LinkedHashList因为查询速度的原因也使用了散列,但是它使用了链表来维护元素的插入顺序。
Set具有与Collection完全相同的接口。
比较器:想TreeSet的构造器传入String.CASE_INSENTIVE_ORDER比较器
Set
11.10 Map
get()方法将返回null(这表示该数字第一次被找到)。否则,get()方法将产生与该键相关联的Integer值。
通过String来执行查找,containsKey()和containsValue()测试一个Map,查看它是否包含某个键或某个值。
Map与数组和其他的Collection一样,可以扩展为多维。Java中的“?”表示泛型。
11.11 Queue
队列是一个先进先出(FIFO)的容器。LinkedList可以用作Queue的一种实现。
offer()方法是与Queue相关的方法之一,它在运行的情况下,将一个元素插入到队尾。或者返回false。
peek()和element()都都将在不移除的情况下返回队头。但是peek()方法在队列为空时返回nul,
而element()会抛出NoSuchElementExecption异常。poll()和remove()方法将移除返回队头,但是poll()在队列为空时返回null,remove()方法则返回NoSuchElementExectpion异常。
11.11.1 PriorityQueue
队列规则是指在给定一组队列中的元素的情况下,确定下一个弹出队列的元素的规则,先进先出声明的是下一个元素应该是等待时间最长的元素。
优先级队列声明下一个弹出元素时最需要的元素(具有最高的优先级)。PriorityQueue是这种行为的一种自动实现。
默认的排序将使用对象在队列中的自然顺序,通过提供自己的Comparator来修改这个顺序。
PriorityQueue可以确保当你调用peek()、poll()和remove()方法时,获取的元素将是队列中优先级最高的元素。
最小的值的优先级最高,空格也可以算是值,比字母的优先级高。
11.12 Collection 和Iterator
Collection是描述所有序列容器的共性的根接口,即因为要表示其他若干个接口的共性而出现的接口。
使用接口描述的一个理由是它可以使我们能够创建更通用的代码。
当实现一个不是Collection的外部类时,由于让它去实现Collection接口可能非常困难或麻烦,因此使用Iterator是比较好的选择。例如我们通过继承一个持有Pet对象的类来创建一个Collection的实现,那么我们必须实现所有的Collection方法,即使我们再display()方法中不必使用它们,也必须如此,尽管这可以通过继承AbstractCollection而很容易地实现,但是你无论如何还是要被强制去实现iteratro()和size(),以便提供AbstractCollection没有实现,但是AbstractCollection中的其他方法会使用到的方法。remove()方法是一个“可选操作”.
生成Iterator是将队列与消费队列的方法连接在一起耦合度最小的方式,并且与实现Collection相比,它在序列类上所施加的约束也少得多。
11.13 Foreach与迭代器
foreach可以作用与所有Collection对象时因为Iteratorable的接口,该接口包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。
iterator()与继承AbstractCollection相比,花费的代价只有略微减少,但是如果你的类已经继承了其他的类,那么你就不能继承AbstractCollection了,在这种情况下,要实现Collection,就必须实现该接口中的所有方法。
iterator()方法返回的是实现了Iterator
11.13.1 适配器方法惯用法
如果现有一个Iterable类,你想要添加一个或多种在foreach语句中使用这个类的方法,应该怎么做呢?例如:假设你希望可以选择以向前的方向或是向后的方向迭代一个单词列表。如果直接继承这个类,并覆盖iterator()方法,你只能替换现有的方法,而不能实现选择。
一种解决方案是所谓适配器方法的惯用法。“适配器”部分来自于设计模式,因为你必须提供特定接口以满足foreach语句,当你有一个接口并需要另一个接口时,编写适配器就可以解决问题,这里我在默认的前向迭代器的基础上,添加产生反向迭代器的能力,因此我不能使用覆盖,添加了一个能够产生Iterable对象的方法,改对象可以用于foreach语句。
Arrays.asLisst()产生的List对象会使用底层数组作为其物理实现。如果不想原来的数组被修改,那必须在另一个容器中创建一个副本。
11.14 总结
Java提供了大量持有对象的方式:
1)数组将数字与对象联系起来。它保存类型明确的对象,查询对象时,不需要对结果做类型转换。它可以是多维的,可以保存基本类型的数据。但是,数组一旦生成,其容量就不能改变。
2)Collection保存单一的元素,而Map保存相关联的键值对。有了Java的泛型,你就可以指定容器中存放的对象类型,因此你就不会将错误类型的对象放置到容器中,并且在从容器中获取元素时,不必进行类型转换。各种Collection和各种Map都可以在你向其中添加更多的元素时,自动调整其尺寸。容器不能持有基本类型,但是自动包装机制会仔细地执行基本类型到容器中所持有的包装器类型之间的双向转换。
3)像数组一样,List也建立数字索引与对象的关联,因此,数组和List都是排好序的容器。List能够自动扩充容量。
4)如果要进行大量的随机访问,就使用ArrayList;如果要经常从表中间插入或删除元素,则应该使用LinkedList.
5)各种Queue以及栈的行为为,有LinkedList提供支持。
6)Map是一种将对象(而非数字)与对象相关联的设计。HashMap设计用来快速访问;而TreeMap保持“键”始终处于排序状态,所以没有HashMap块。LinkedHashMap保持元素插入的顺序,但是也通过散列提供了快速访问能力。
7)Set不接受重复元素。HashSet提供最快的查询速度,而TreeSet保持元素处于排序状态。
LinkedHashSet以插入顺序保持元素。
8)新程序中不应该使用过时的Vector、Hashtable和Stack。