第21章 集合框架
通过前面几章的介绍,读者对Java的基本特性已经有了一定了解。但是关于数据的存储目前还没有一个很好的工具,比如对象集合如何保存,可以使用它们使得在添加、查找、删除对象时候的效率更高。这时候就需要使用集合,使用了这些类和接口,就能方便操作大量的数据。前面讨论了关于数组的感念,其实数组就是一黾希撬泻芏嗖环奖愕牡胤健1热纾憾ㄒ逡桓鍪榈氖焙颍匦肷昝魉某ざ龋橹兄荒艽娣乓恢掷嘈偷氖荩庋钥⒑懿焕Java提供了集合框架来解决这写问题。
21.1 集合概述
本节的主要内容是介绍什么是集合框架,以及现在集合框架的一些新的特性,如泛型、自动装箱与自动拆性、for-each循环等内容。通过本节的学习,读者可以对Java的集合框架有一个大体的概念上的认识,方便后边的学习。
21.1.1 什么是集合框架
Java的集合框架是java.util包中提供的一系列工具,它为程序处理对象组提供了标准的方式。在最初的Java版本中是不包括集合框架的,在添加集合框架之前时是使用一些专门的类,如Vector、Stack、Properties类来完成对象存储等一系列的操作。这些类尽管功能已经能够满足操作的要求,但是它们缺乏一个集中、统一的形式,例如操作Vector类和操作Properties类的过程完全是不同的。另外一个问题就是这些类是不易扩展的,集合框架就是来解决这些问题的。
21.1.2 集合框架新特性
从J2SE 5开始,Java为了进一步提高性能,提供了一系列新的特性,如泛型、自动装箱自动拆箱、for each循环等。尽管这些特性不仅仅是集合框架的原因才加入到Java中来,但不可否认的一点是它们的出现,进一步增强了集合框架的功能。
1. 泛型
2. 自动装箱与自动拆箱
3. for each 循环
21.2 集合接口
集合框架通过定义接口的方式来规范集合类的操作。在Java的集合框架中,主要的接口有Collection接口、List接口、Queue接口、Set接口、SortedSet接口。其中Collection接口处于集合框架层次结构的顶部。本节的主要内容就是对这个几个接口进行介绍。List接口、Queue接口和Set接口都直接继承自Collection接口,SortedSet直接继承自Set接口。
21.2.1 Collection接口
Collection接口是集合框架的基础。它处于整个集合框架层次的顶端,即任何的集合类和接口必须都实现或是扩展该接口。
21.2.2 List接口
List接口扩展自Collection接口,扩展该接口的集合类是一个以列表形式存储的集合。实现此接口类可以列表中每个元素的插入位置进行精确地控制。可以根据元素的整数索引(在列表中的位置)访问元素,也可以列表中的元素。需要注意的一点是列表通常允许重复的元素。如果列表本身允许null元素的话,通常它们允许包含多个null元素。
21.2.3 Set接口
Set接口扩展自Collection接口,它定义了一个组,声明该集合不允许出现相同的元素。当往列表中插入已经存在的元素的时候,该方法会返回false。该接口的定义如下:
public interface Set<E>extends Collection<E>
其中E为该Set保存元素的类型。该接口并没有定义自己的方法,它所有的方法都是从接口Collection继承而来。
21.2.4 Queue接口
Queue接口扩展自Collection接口,它声明了一个队列集合。队列通常以先进先出的形式存储元素。
使用该接口的时候,需要注意以下几点:首先,由于队列是先进先出结构的,只能删除队列的头元素。第二点是有两个方法都能删除头元素,poll方法和remove方法,这两个方法的不同在于poll方法在队列为空的时候会返回null,而remove方法会抛出异常。第三点是该方法有两个方法能获得但是不删除队列的头元素,peek方法和element方法。不同在于,peek方法在队列为空的时候会返回null,而element方法在队列为空的时候会产生异常。第四点,跟Collection接口不同的是,当通过off方法插入元素失败的时候并不会产生异常。而是返回false,所以该方法的使用性更高。
21.2.5 SortedSet接口
SortedSet接口扩展自Set并且定义了自己的一些方法。它采用红黑树的数据结构对其元素排序,保证迭代器能够按照元素递增顺序遍历集合。需要注意的一点是,插入有序集合的所有元素都必须实现Comparable接口,这样集合才能对其进行排序。
21.3 集合实现类
在前一节已经介绍过集合类的主要几个接口,这样就能对集合框架有一个大概的认识。本节的主要内容是介绍几个常用的集合具体实现类。通过本节的学习,用户就可以使用集合类来高效的存储和获取对象了。
21.3.1 ArrayList类
ArrayList类是支持动态增长的动态数组列表,扩展自Abstract接口,并实现List接口。该类是一个泛型类,它的声明如下:
public class ArrayList<E>extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, Serializable
其中类型参数E是在数组列表中要存放的数据类型。ArrayList跟数组的实现一样,可以高效的实现在其中存放元素的检索(基于位置的)。比数组更好的是当一个ArrayList被创建的时候,它具备了一定的存放数据的能力。但是当满了以后,它会自动的扩充自己的容量,让自己能够容纳更多的元素。
21.3.2 LinkedList类
LinkedList类是一个以链表形式实现的集合类。该类扩展了AbstractSequentiaList,并实现了List接口和Queue接口。
LinkedList除了继承得来的方法外,还自己定义了一些方法以增强自己的功能。这些操作主要是对链表数据结果的操作。如添加元素到链表列表头部,使用addFirst()方法,添加元素到链表列表尾部使用addLast()方法。
21.3.3 HashSet类
HashSet类扩展了AbstractSet并实现Set接口,由哈希表支持。它实际上是一个HashMap实例。哈希表是通过使用散列的机制来进行信息存储的,在该种方式中键的内容用于标志一个唯一的值,对元素的索引是通过键的索引来实现的,所以该中结构的查找速度是很快的。在它不保证集合的迭代顺序,特别是它不保证该顺序恒久不变。此类允许使用null元素。
21.4 迭代器
集合是用来存放元素的,当元素的数量非常大的时候,访问就比较复杂了。Java提供了迭代器来进行集合的便利,迭代器是实现了Iterator接口的对象。通过迭代器的使用可以很便捷的访问存放在集合中元素。
21.4.1 迭代器介绍
Iterator接口的声明如下:
public interface Iterator<E>
它声明的方法主要有以下几个:
boolean hasNext() 如果迭代器仍有元素可以迭代,则返回 true。
E next() 该方法返回迭代的下一个元素。当没有元素可以迭代的时候,该方法在没有元素可以访问的时候会抛出NoSuchElementException异常。
void remove() 删除当前元素,迭代器不支持remove操作会抛出UnsupportedOperationException。如果尚未调用next方法,或者在上一次调用next方法之后已经调用了remove方法会抛出IllegalStateException异常。
21.4.2 迭代器操作过程
迭代器的使用是十分有规律的,要首先从集合类提供的iterator()方法获得迭代器,这时候迭代器处于集合的开始位置。通过该迭代器就可以一个个访问元素了。它的一般步骤如下:
(1)通过集合iterator()方法获得一个指向集合开始处的迭代器。
(2)以hasNext()方法作为循环的条件,直到没有元素的时候循环停止。
(3)在循环的时候,通过next()方法得到每个元素。
21.4.3 for-each循环访问集合
除了使用迭代器来访问集合,还可以使用for-each循环来访问集合的元素。它的格式如下:
for(元素类型 element : 集合)
{
//操作
}
在操作中可以使用element表示访问到的元素。
21.5 映射接口
映射是用来存储键值对对象的结构。在该结构中,给定其键值就可以找到其对应的值,其中键和值都是对象,键值必须唯一。映射类的设计跟集合类有相似之处,它定义了一些接口来规范映射的操作标准。这些主要的接口有Map接口、Map.Entry接口、SortedMap接口。
21.5.1 Map接口
Map接口将唯一的键值映射到值。在该映射中不能有重复的键值,每个键只能映射到一个值。其中的键和值都是对象。
由于映射跟集合是不同的。而这两种机制又经常需要交互操作,所以Java提供了一些方法来获得键或值的集合。使用entrySet()方法,能够得到该映射的集合视图,返回一个包含映射重元素的集合。使用keySet()方法,能够得到映射中所有键值的集合视图。而使用values()方法可以得到映射中所有值的集合视图。
21.5.2 SortedMap接口
SortedMap接口扩展了Map接口,它确保该映射按照键以升序的方式来保存键值对。可以按照键的自然顺序进行排序,或者通过创建有序映射时提供的比较器进行排序。
由于该接口是一个有序的映射,所以其提供了一系列的方法可以很方便的取得映射的子映射,主要使用三个方法:headMap()、subMap()、tailMap()。而其提供的firstKey()和lastKey()可以很方便的获得映射的头或尾的键值。实现该接口的类为TreeMap类,在后边会对该类有介绍,这里不再赘述。
21.5.3 Map.Entry接口
使用该接口可以使用映射项。Map接口声明的entrySet()方法返回的就是包含所有键值对的集合,每个结合元素就是一个Map.Entry对象。
该接口定义的主要方法如下:
K getKey() 获得映射项的键值。该方法会抛出异常:IllegalStateException说明已经从底层映射中移除了该项,则实现映射的类可能抛出此异常。
V getValue() 返回此映射项的值。该方法会抛出异常:IllegalStateException说明已经从底层映射中移除了该项,则实现映射的类可能抛出此异常。
V setValue(V value) 用参数指定的值替换该映射项的值,UnsupportedOperationException说明底层映射不支持put操作。ClassCastException说明指定值的类不允许将该值存储在底层映射中。llegalArgumentException说明此值的某些方面不允许将其存储在底层映射中。NullPointerException说明底层映射不允许存放null值,并且指定的值为null。IllegalStateException说明已经从底层映射中移除了该项,则可能抛出此异常。
int hashCode() 返回此映射项的散列值。
21.6 映射类
Java提供了一系列的类来实现映射接口。主要有这些:AbstractMap类、EnumMap类、HashMap类、TreeMap类、WeakHashMap类、LinkedHashMap类。其中AbstractMap是所有具体实现类的超类,它实现了多数的Map接口。本节主要对常用的几个映射类进行简单的介绍。
21.6.1 HashMap类
HashMap类是使用哈希表实现Map接口的类。通过哈希表的实现,可以实现get()和put()方法的执行时间不随集合的增大而变长。需要注意的一点是此实现不是同步的。如果多个线程同时访问此映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。外部同步的意思就是要用户在程序中保证其同步。
21.6.2 TreeMap类
TreeMap类是通过树结构来实现Map接口的。此类保证了映射按照升序顺序排列关键字,并且支持快速查找。该类的常用构造函数如下:
public TreeMap():该方法构造一个新的空映射,该映射按照键的自然顺序排序。
public TreeMap(Comparator<? super K> c):该方法构建一个空的映射,该映射的比较器由c指定。
public TreeMap(Map<? extends K,? extends V> m):该方法构建一个映射,该映射包含m的内容。该方法会抛出异常:ClassCastException说明m中的键不是Comparable或者不是可相互比较的。 NullPointerException说明指定的映射为 null。
public TreeMap(SortedMap<K,? extends V> m):构造一个新的映射,包含的映射关系与给定的SortedMap相同。该映射按照相同的排序方式进行排序。如果m为null会抛出NullPointerException异常。
21.7 小结
集合框架是Java提供的强大对象管理机制。使用它可以很方便的管理对象,但是Java提供的集合框架机制是很复杂的。本章主要是从整体结构上介绍了集合框架,然后都会其中的一些常用类进行了介绍。通过本章读者应该能够建立一个清晰的框架来认识Java集合框架,在需要进行对象管理的时候选择合适的集合类。