Java 集合类是一种特别有用的工具类,可以用于存储数量不等的多个对象,并可以实现常用的数据结构,如栈、队列等。除此之外,Java 集合还可用于保存具有映射关系的关联数组。Java 集合大致可以为 Set、List 和 Map 三种体系,其中 Set 代表无序、不可重复的集合;List 代表有序、重复的集合;而 Map 则代表具有映射关系的集合。从 Java 5 以后,Java 又增加了 Queue 体系集合,代表一种队列集合实现。
Java 集合就像一种容器,我们可以把多个对象(实际上是对象的引用,但习惯上都称为对象)“丢进”该容器中。在 Java 5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理;从 Java 5 增加了泛型以后,Java 集合可以记住容器中对象的数据类型,从而可以编写更简洁、健壮的代码。
在编程时,常常需要集中存放多个数据。当然我们可以使用数组来保存多个对象,但数组长度不可变化,一旦在初始化数组时指定了数组长度,这个数组长度就是不可变的,如果需要保存数量变化的数据,数组就优点无能为力了;而且数组无法保存具有映射关系的数据,如成绩表:语文--79,数学--80,这种数据看上去像两个数组,但这两个数组的元素之间有一定的关联关系。
为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),Java 提供了集合类。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。所有的集合类都位于 java.util 包下,后来为了处理多线程环境下的并发安全问题,Java 5 还在 java.util.concurrent 包下提供了一些多线程支持的集合类。
集合类和数组不一样,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量);而集合是只能保存对象(实际上只是保存对象的引用变量,但通常习惯上认为集合里保存的是对象)。
Java 的集合类主要由两个接口派生而出:Collection 和 Map,Collection 和 Map 是 Java 集合框架的根接口,这两个接口又包含了一些子接口或实现类。如图8.1 所示是 Collection 接口、子接口及其实现类的继承树。
图 8.1 显示了 Collection 体系里的集合,其中粗线圈出的 Set 和 List 接口是 Collection 接口派生的两个子接口,它们分别代表了无序集合和有序集合;Queue 是 Java 提供的队列实现,有点类似于 List,后面章节还会更详细的介绍,此处不再赘述。
如图 8.2 所示是 Map 体系的继承树,所有的 Map 实现类用于保存具有映射关系的数据(也就是前面介绍的关联数组)。
图8.2 显示了 Map 接口的众多实现类,这些实现类在功能、用法上存在一定的差异,但它们都有一个功能特征:Map 保存的每项数组都是 key-value 对,也就是有 key 和 value 两个值组成。就像前面介绍的成绩单:语文-79,数学-80,每项成绩都由两个值组成,即科目名和成绩。对于一张成绩表而言,科目通常不会重复,而成绩是可重复的,通常习惯根据科目来查阅成绩,而不会根据成绩来查询科目。Map 于此类似,Map 里的 key 是不可重复的,key 用于标识集合里的每项数据,如果需要查阅 Map 中的数据时,总是根据 Map 的 key 来获取。
根据图 8.1 和图 8.2 中粗线标识的 4 个接口,我们可以把 Java 的所有集合分成三大类,其中 Set 集合类似于一个罐子,把一个对象添加到 Set 集合时,Set 集合无法记住添加这个元素的顺序,所以 Set 里的元素不能重复(否则系统无法准备识别这个元素);List 集合非常像一个数组,它可以记住每个添加元素的顺序,只是 List 的长度可变。Map 集合也像一个罐子,只是它里面的每项数据都由两个值组成。图 8.3 显示了这三种集合的示意图。
从图 8.3 中可以看出,如果访问 List 集合中的元素,可以直接根据元素的索引来访问;如果访问 Map 集合中的元素,可以根据每项元素的 key 来访问其 value;如果访问 Set 集合中的元素,则只能根据元素本身来访问(这也是 Set 集合里元素不允许重复的原因)。
对于 Set、List、Queue 和 Map 四种集合,最常用的实现类在 图 8.1、图 8.2 中以灰色区域覆盖,分别是 HashSet、TreeSet、ArrayList、ArrayDeque、LinkedList 和 HashMap、TreeMap 等实现类。