Java Collections Framework
一、集合类的引入
通常,程序总是根据运行时才知道某些条件去创建对象,在此之前,不知道所需对象的数量,甚至不知道确切的类型。为解决这个普遍的编程问题,需要在任何时刻和任何位置创建任何数量的对象,就不能创建命名的引用来持有每一个对象。 MyType aReference;
Java有多重形式保存对象,如数组。但是数组有固定的尺寸,在更一般的情况中,很多的时候是你在程序中不知道需要创建多少个对象,或者是否需要更复杂的方式来存储对象,因此数组尺寸固定这一限制过于受限了。
所以在Java实用类库中专门提供了一套动态对象数组的操作类——类集框架,在Java中类集框架实际上也就是对数据结构的Java实现。集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法。其中最基本的类型是Collection(List,Set,Queue),Map。
容器类只能容纳对象引用,基本类型会自动装箱,一个数组,既可以直接容纳基本类型的数据,亦可容纳指向对象的引用。
二、什么是集合框架
那么有了集合的概念,什么是集合框架呢?集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法。
接口:即表示集合的抽象数据类型。接口提供了让我们对集合中所表示的内容进行单独操作的可能。
实现:也就是集合框架中接口的具体实现。实际它们就是那些可复用的数据结构。
算法:在一个实现了某个集合框架中的接口的对象身上完成某种有用的计算的方法,例如查找、排序等。这些算法通常是多态的,因为相同的方法可以在同一个接口被多个类实现时有不同的表现。
三、使用集合框架的好处
1、它减少了程序设计的辛劳
集合框架通过提供有用的数据结构和算法使你能集中注意力于你的程序的重要部分上,而不是为了让程序能正常运转而将注意力于低层设计上。通过这些在无关API之间的简易的互用性,使你免除了为改编对象或转换代码以便联合这些API而去写大量的代码。
2、它提高了程序速度和质量
集合框架通过提供对有用的数据结构和算法的高性能和高质量的实现使你的程序速度和质量得到提高。因为每个接口的实现是可互换的,所以你的程序可以很容易的通过改变一个实现而进行调整。另外,你将可以从写你自己的数据结构的苦差事中解脱出来,从而有更多时间关注于程序其它部分的质量和性能。
3、减少去学习和使用新的API 的辛劳
许多API天生的有对集合的存储和获取。在过去,这样的API都有一些子API帮助操纵它的集合内容,因此在那些特殊的子API之间就会缺乏一致性,你也不得不从零开始学习,并且在使用时也很容易犯错。而标准集合框架接口的出现使这个问题迎刃而解。
4、减少了设计新API的努力
设计者和实现者不用再在每次创建一种依赖于集合内容的API时重新设计,他们只要使用标准集合框架的接口即可。
5、集合框架鼓励软件的复用
对于遵照标准集合框架接口的新的数据结构天生即是可复用的。同样对于操作一个实现了这些接口的对象的算法也是如此。
四、Java1.2之前的容器类库
在Java2之前,Java是没有完整的集合框架的。它只有一些简单的可以自扩展的容器类,比如Vector,Stack,HashTable等。
1、Vector
Vector中包含的元素可以通过一个整型的索引值取得,它的大小可以在添加或移除元素时自动增加或缩小。然而,Vector的设计却存在极多缺陷(下面会说到)。
2、Stack
Stack是一种后进先出(LIFO)的堆栈序列,重要特点是先放入的东西最后才能被取出。
3、Hashtable
Hashtable与Java2中的Map类似,可以看成一种关联或映射数组,可以将两个或多个毫无关系的对象相关联,与数组不同的是它的大小可以动态变化。
五、Java2中的容器类库
简化后
六、公用类
集合框架中还有两个很实用的公用类:Collections和Arrays。
6.1、Collections
Collections提供了对一个Collection容器进行诸如排序、复制、查找和填充等一些非常有用的方法,Arrays则是对一个数组进行类似的操作。
6.2、Arrays类
java.util里面有一个Arrays类,它包括了一组可用于数组的static方法,这些方法都是一些实用工具。其中有四个基本方法:用来比较两个数组是否相等的equals();用来填充的fill();用来对数组进行排序的sort();以及用于在一个已排序的数组中查找元素的binarySearch()。所有这些方法都对primitive和Object进行了重载。此外还有一个asList()方法,它接受一个数组,然后把它转成一个List容器。
七、主要集合类
Java中类集框架实际上也就是对数据结构的Java实现。其中最基本的类型是Collection(List,Set,Queue)、Map。
7.1、List
List的最重要的特征就是有序;它会确保以插入顺序保存元素,允许重复。
List在Collection的基础上添加了大量方法,使之能在序列中间插入和删除元素。(只对LinkedList推荐使用)List可以制造ListIterator对象,你除了能用它在List的中间插入和删除元素之外,还能用它沿两个方法遍历List。
7.1.1、ArrayList
ArrayList*:一个用数组实现的List。能进行快速的随机访问,但是往列表中间插入和删除元素的时候比较慢。
7.1.2、LinkedList
内部实现是链表,它适合于在链表中间需要频繁进行插入和删除操作。
LinkedList:对顺序访问进行了优化。在List中间插入和删除元素的代价也不高。随机访问的速度相对较慢。此外它还有addFirst(),addLast(),getFirst(),getLast(),removeFirst()和removeLast()等方法,你能把它当成栈(stack),队列(queue)或双向队列(deque)来用。
7.1.3、LinkedList的用途
用LinkedList做一个栈
“栈(stack)”有时也被称为“后进先出”(LIFO)的容器。就是说,最后一个被“压”进栈中的东西,会第一个“弹”出来。LinkedList的方法能直接实现栈的功能,所以你完全可以不写Stack而直接使用LinkedList。
用LinkedList做一个队列
队列(queue)是一个“先进先出”(FIFO)容器。也就是,你把一端把东西放进去,从另一端把东西取出来。所以你放东西的顺序也就是取东西的顺序。LinkedList有支持队列的功能的方法,所以它也能被当作Queue来用。
用LinkedList做一个deque(双向队列)
它很像队列,只是你可以从任意一端添加和删除元素。
7.2、Set
Set接口也是Collection的一种扩展,与List不同的是,在Set中的对象元素不能重复。加入Set的每个元素必须是唯一的;否则,Set是不会把它加进去的。它的常用具体实现有HashSet和TreeSet类、LinkedHashSet类。他们保存对象的顺序是和不一样的,这是因为它们是用不同的方法来存储和查找元素的。(TreeSet用了一种叫红黑树的数据结构【red-black tree datastructure】来为元素排序,而HashSet则用了“专为快速查找而设计”的散列函数。LinkedHashSet在内部用散列来提高查询速度,但是它看上去像是用链表来保存元素的插入顺序的。)
7.2.1、HashSet
为优化查询速度而设计的Set,能快速定位一个元素。要放进HashSet里面的Object还得定义hashCode()。
7.2.2、TreeSet
TreeSet则将放入其中的元素按序存放,这就要求你放入其中的对象是可排序的,这就用到了集合框架提供的另外两个实用类Comparable和Comparator。一个类是可排序的,它就应该实现Comparable接口。
7.2.3、LinkedHashSet
一个在内部使用链表的Set,既有HashSet的查询速度,又能保存元素被加进去的顺序(插入顺序)。用Iterator遍历Set的时候,它是按插入顺序进行访问的。
7.3、Map
Map是一种把键对象和值对象进行关联的容器,一个Map容器中的键对象不允许重复,这是为了保持查找结果的一致性;如果有两个键对象一样,那你想得到那个键对象所对应的值对象时就有问题了,可能你得到的并不是你想的那个值对象,结果会造成混乱,
键和值的关联很简单,用put(Objectkey,Objectvalue)方法即可将一个键与一个值对象相关联。用get(Object key)可得到与此key对象所对应的值对象。也可以用containsKey()和containsValue()测试Map是否包含有某个键或值。
Java标准类库里有好几种Map:HashMap,TreeMap,LinkedHashMap
7.3.1、HashMap:
基于hash表的实现。(用它来代替Hashtable)提供时间恒定的插入与查询。在构造函数种可以设置hash表的capacity和load factor。可以通过构造函数来调节其性能。
7.3.2、TreeMap
基于红黑树数据结构的实现。它们是按顺序排列的。TreeMap是Map中唯一有subMap()方法的实现。这个方法能让你获取这个树中的一部分。
7.3.3、LinkedHashMap
很像HashMap,但是用Iterator进行遍历的时候,它会按插入顺序或最先使用的顺序(least-recently-used(LRU)order)进行访问。除了用Iterator外,其他情况下,只是比HashMap稍慢一点。用Iterator的情况下,由于是使用链表来保存内部顺序,因此速度会更快。
7.4、Queue:
队列通常(但并非一定)以 FIFO(先进先出)的方式排序各个元素。
7.4.1、LinkedList:
提供了方法支持队列的行为,因此可以作为Queue的一种实现,将LinkedList向上转型为Queue使用。
7.4.2、PriorityQueue
一个基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的Comparator进行排序,具体取决于所使用的构造方法。优先级队列不允许使用null元素。依靠自然顺序的优先级队列还不允许插入不可比较的对象(这样做可能导致ClassCastException)。