package com.tudou.Collection;
/**
 * List:ArrayList LinkedList 元素可以重复
 * ArrayList:长于随机访问[插入和删除代价高] LinkedList长于插入和删除[随机访问慢] 可向上转型为Queue队列
 * 
 * Set:HashSet TreeSet LinkedHashSet 元素不能重复
 * HashSet无序[散列函数] 最快查找 TreeSet升序[红黑树],LinkedHashSet按照插入顺序排序[散列,链表]
 * 
 * Map:HashMap TreeMap LinkedHashMap 键值对保存数据
 * HashMap 最快查找 TreeMap键值升序 LinkedHashMap按照插入顺序保存键,同时有HashMap查询速度
 */
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;

public class PrintContainers {
	static Collection<String> fill(Collection<String> collection) {
		collection.add("rat");
		collection.add("cat");
		collection.add("dog");
		collection.add("dog");
		return collection;
	}
	
	static Map<String,String> fill(Map<String,String> map){
		map.put("rat", "Fuzzy");
		map.put("cat", "Rags");
		map.put("dog", "Bosco");
		map.put("dog", "Spot");
		return map;
	}
	static void print(Object obj){
		System.out.println(obj);
	}
	public static void main(String[] args) {
		print(fill(new ArrayList<String>()));
		print(fill(new LinkedList<String>()));
		print(fill(new HashSet<String>()));
		print(fill(new TreeSet<String>()));
		print(fill(new LinkedHashSet<String>()));
		print(fill(new HashMap<String,String>()));
		print(fill(new TreeMap<String,String>()));
		print(fill(new LinkedHashMap<String,String>()));
	}
}

控制台:

[rat, cat, dog, dog]
[rat, cat, dog, dog]
[cat, dog, rat]
[cat, dog, rat]
[rat, cat, dog]
{cat=Rags, dog=Spot, rat=Fuzzy}
{cat=Rags, dog=Spot, rat=Fuzzy}
{rat=Fuzzy, cat=Rags, dog=Spot}


用“集合框架”设计软件时,记住该框架四个基本接口的下列层次结构关系会有用处:


Collection 接口是一组允许重复的对象。 Set 接口继承 Collection ,但不允许重复。 List 接口继承 Collection ,允许重复,并引入位置下标。 Map 接口既不继承 Set 也不继承 Collection , 存取的是键值对

我们以下面这个图表来描述一下常用的集合的实现类之间的区别:

Collection/Map

接口

成员重复性

元素存放顺序(Ordered/Sorted)

元素中被调用的方法

基于那中数据结构来实现的

HashSet

Set

Unique elements

No order

equals()

hashCode()

Hash表

LinkedHashSet

Set

Unique elements

Insertion order

equals()

hashCode()

Hash表和双向链表

TreeSet

SortedSet

Unique elements

Sorted

equals()

compareTo()

平衡树(Balanced tree)

ArrayList

List

Allowed

Insertion order

equals()

数组

LinkedList

List

Allowed

Insertion order

equals()

链表

Vector

List

Allowed

Insertion order

equals()

数组

HashMap

Map

Unique keys

No order

equals()

hashCode()

Hash表

LinkedHashMap

Map

Unique keys

Key insertion order/Access order of entries

equals()

hashCode()

Hash表和双向链表

Hashtable

Map

Unique keys

No order

equals()

hashCode()

Hash表

TreeMap

SortedMap

Unique keys

Sorted in key order

equals()

compareTo()

平衡树(Balanced tree)

Collection

├List

│├LinkedList

│├ArrayList (异步,线程不安全,空间用完时自动增长原容量一半)

│└Vector (同步,线程安全,空间用完时自动增长原容量一倍)

│ └Stack

└Set

├HashSet

└TreeSet

Map

├Hashtable

├HashMap

├WeakHashMap

└TreeMap

Map接口:

|

+ -- WeakHashMap: 以弱键 实现的基于哈希表的 Map。在 WeakHashMap 中,当某个键不再正常使用时,将自动移除其条

| 目。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终

| 止,然后被回收。丢弃某个键时,其条目从映射中有效地移除,因此,该类的行为与其他的 Map 实现有所不同。此实现

| 不是同步的。

|

+ -- TreeMap:该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的

| 构造方法。此实现不是同步的。

|

+ -- HashMap:基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了

| 非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺

| 序恒久不变。此实现不是同步的。

|

+-- SortedMap: 进一步提供关于键的总体排序 的 Map。该映射是根据其键的自然顺序进行排序的,或者根据通常在创建有

序映射时提供的 Comparator 进行排序。对有序映射的 collection 视图(由 entrySet、keySet 和 values 方法返回

)进行迭代时,此顺序就会反映出来。要采用此排序方式,还需要提供一些其他操作(此接口是 SortedSet 的对应映

射)。

Collection接口:

|

+ -- Set接口:一个不包含重复元素的 collection。更正式地说,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并

| | 且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象。

| |

| + -- HashSet:此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;

| | 特别是它不保证该顺序恒久不变。此类允许使用 null 元素。此类为基本操作提供了稳定性能,此实现不是同

| | 步的。

| |

| + -- LinkedHashSet:具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在

| | 于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set

| | 中 的顺序(插入顺序)进行迭代。注意,插入顺序不 受在 set 中重新插入的 元素的影响。此实现不是同步

| | 的。

| |

| + -- TreeSet:基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时

| 提供的 Comparator 进行排序,具体取决于使用的构造方法。此实现为基本操作(add、remove 和 contains)

| 提供受保证的 log(n) 时间开销。此实现不是同步的。

|

+ -- List接口:有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户

| 可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

|

+ -- ArrayList:List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。

| 除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于

| Vector 类,除了此类是不同步的。)每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数

| 组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增

| 长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。此实现不是同步的。

|

+ -- LinkedList:List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实

| 现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方

| 法。这些操作允许将链接列表用作堆栈、队列或双端队列。提供先进先出队列操作(FIFO)。此实现不是同步的。

|

+ -- Vector:Vector 类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是

,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。此实现是同步的.

1. Collection的功能

下面这张表给出了Collection的所有功能,也就是你能用Set和List做什么事(不包括从Object自动继承过来的方法)。(List还有一些额外的功能。)Map不是继承Collection的,所以我们会区别对待。

boolean add(Object):确保容器能持有你传给它的那个参数。如果没有把它加进去,就返回false。(这是个“可选”的方法,本章稍后会再作解释。)

boolean addAll(Collection):加入参数Collection所含的所有元素。只要加了元素,就返回true。

void clear():清除容器所保存的所有元素。(“可选”)

boolean contains(Object):如果容器持有参数Object,就返回true。

boolean containsAll(Collection):如果容器持有参数Collection所含的全部元素,就返回true。

boolean isEmpty():如果容器里面没有保存任何元素,就返回true。

Iterator iterator():返回一个可以在容器的各元素之间移动的Iterator。

boolean removeAll(Collection):删除容器里面所有参数Collection所包含的元素。只要删过东西,就返回true。(“可选”)

boolean retainAll(Collection):只保存参数Collection所包括的元素(集合论中“交集”的概念)。如果发生过变化,则返回true。(“可选”)

int size():返回容器所含元素的数量。

Object[] toArray():返回一个包含容器中所有元素的数组。

Object[] toArray(Object[] a):返回一个包含容器中所有元素的数组,且这个数组不是普通的Object数组,它的类型应该同参数数组a的类型相同(要做类型转换)。

注意,这里没有能进行随机访问的get()方法。这是因为Collection还包括Set。而Set有它自己的内部顺序(因此随即访问事毫无意义的)。所以如果你要检查Collection的元素,你就必须使用迭代器。

2.List的功能

List的基本用法事相当将但的。虽然绝大多数时候,你只是用add()加对象,用get()取对象,用iterator()获取这个序列的Iterator,但List还有一些别的很有用的方法。

实际上有两种List:擅长对元素进行随机访问的,较常用的ArrayList,和更强大的LinkedList。LinkedList不是为快速的随机访问而设计的,但是它却有一组更加通用的方法。

Lisk(接口):List的最重要的特征就是有序;它会确保以一定的顺序保存元素。List在Collection的基础上添加了大量方法,使之能在序列中间插入和删除元素。(只对LinkedList推荐使用。)List可以制造ListIterator对象,你除了能用它在List的中间插入和删除元素之外,还能用它沿两个方法遍历List。

ArrayList*:一个用数组实现的List。能进行快速的随机访问,但是往列表中间插入和删除元素的时候比较慢。ListIterator只能用在反向遍历ArrayList的场合,不要用它来插入和删除元素,因为相比LinkedList,在ArrayList里面用ListIterator的系统开销比较高。

LinkedList:对顺序访问进行了优化。在List中间插入和删除元素的代价也不高。随机访问的速度相对较慢。(用ArrayList吧。)此外它还有addFirst(),addLast(),getFirst(),getLast(),removeFirst()和removeLast()等方法(这些方法,接口和基类均未定义),你能把它当成栈(stack),队列(queue)或双向队列(deque)来用。

记住,容器只是一个存储对象的盒子。如果这个笑盒子能帮你解决所有的问题,那你就用不着取管它事怎么实现的(在绝大多数情况下,这是使用对象的基本概念)。如果开发环境里面还有一些别的,会造成固定的性能开销的因素存在,那么ArrayList和LinkedList之间的性能差别就会变得不那么重要了。你只需要它们中的一个,你甚至可以想象有这样一种“完美”的抽象容器;它能根据用途,自动地切换其底层的实现。

用LinkedList做一个栈

“栈(stack)”有时也被称为“后进先出”(LIFO)的容器。就是说,最后一个被“压”进栈中的东西,会第一个“弹”出来。同其他Java容器一样,压进去和弹出来的东西都是Object,所以除非你只用Object的功能,否则就必须对弹起来的东西进行类型转换。

LinkedList的方法能直接实现栈的功能,所以你完全可以不写Stack而直接使用LinkedList。

如果你只想要栈的功能,那么继承就不太合适了,因为继承出来的是一个拥有LinkedList的所有方法的类。

用LinkedList做一个队列

队列(queue)是一个“先进先出”(FIFO)容器。也就是,你把一端把东西放进去,从另一端把东西取出来。所以你放东西的顺序也就是取东西的顺序。LinkedList有支持队列的功能的方法,所以它也能被当作Queue来用。

还能很轻易地用LinkedList做一个deque(双向队列)。它很像队列,只是你可以从任意一端添加和删除元素。

Vector类

  Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。

Stack 类

  Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

3.Set的功能

Set的接口就是Collection的,所以不像那两个List,它没有额外的功能。实际上Set确确实实就是一个Collection--只不过行为方式不同罢了。(这是继承和多态性的完美运用:表达不同地行为。)Set会拒绝持有多个具有相同值的对象的实例(对象的“值”又是由什么决定的呢?这个问题比较复杂,我们以后会讲)。

Set(接口):加入Set的每个元素必须是唯一的;否则,Set是不会把它加进去的。要想加进Set,Object必须定义equals(),这样才能标明对象的唯一性。Set的接口和Collection的一摸一样。Set的接口不保证它会用哪种顺序来存储元素。

HashSet*:为优化查询速度而设计的Set。要放进HashSet里面的Object还得定义hashCode()。

TreeSet:是一个有序的Set,其底层是一颗树。这样你就能从Set里面提取一个有序序列了。

LinkedHashSet(JDK 1.4):一个在内部使用链表的Set,既有HashSet的查询速度,又能保存元素被加进去的顺序(插入顺序)。用Iterator遍历Set的时候,它是按插入顺序进行访问的。

HashSet保存对象的顺序是和TreeSet和LinkedHashSet不一样的。这是因为它们是用不同的方法来存储和查找元素的。(TreeSet用了一种叫红黑树的数据结构【red-black tree data structure】来为元素排序,而HashSet则用了“专为快速查找而设计”的散列函数。LinkedHashSet在内部用散列来提高查询速度,但是它看上去像是用链表来保存元素的插入顺序的。)你写自己的类的时候,一定要记住,Set要有一个判断以什么顺序来存储元素的标准,也就是说你必须实现 Comparable接口,并且定义compareTo()方法。

SortedSet

SortedSet(只有TreeSet这一个实现可用)中的元素一定是有序的。这使得SortedSet接口多了一些方法:

Comparator comparator():返回Set锁使用的Comparator对象,或者用null表示它使用Object自有的排序方法。

Object first():返回最小的元素。

Object last():返回最大的元素。

SortedSet subSet(fromElement, toElement):返回Set的子集,其中的元素从fromElement开始到toElement为止(包括fromElement,不包括toElement)。

SortedSet headSet(toElement):返回Set的子集,其中的元素都应小于toElement。

SortedSet headSet(toElement):返回Set的子集,其中的元素都应大于fromElement。

注意,SortedSet意思是“根据对象的比较顺序”,而不是“插入顺序”进行排序。

4.Map的功能

ArrayList能让你用数字在一愕嘎对象序列里面进行选择,所以从某种意义上讲,它是将数字和对象关联起来。但是,如果你想根据其他条件在一个对象序列里面进行选择的话,那又该怎么做呢?栈就是一个例子。它的标准是“选取最后一个被压入栈的对象”。我们常用的术语map,dictionary,或 associative array就是一种非常强大的,能在序列里面进行挑选的工具。从概念上讲,它看上去像是一个ArrayList,但它不用数字,而是用另一个对象来查找对象!这是一种至关重要的编程技巧。

这一概念在Java中表现为Map。put(Object key, Object value)方法会往Map里面加一个值,并且把这个值同键(你查找时所用的对象)联系起来。给出键之后,get(Object key)就会返回与之相关的值。你也可以用containsKey()和containsValue()测试Map是否包含有某个键或值。

Java标准类库里有好几种Map:HashMap,TreeMap,LinkedHashMap,WeakHashMap,以及 IdentityHashMap。它们都实现了Map的基本接口,但是在行为方式方面有着明显的诧异。这些差异体现在,效率,持有和表示对象pair的顺序,持有对象的时间长短,以及如何决定键的相等性。

性能时Map所要面对的一个大问题。如果你知道get()时怎么工作的,你就会发觉(比方说)在ArrayList里面找对象会是相当慢的。而这正是 HashMap的强项。它不是慢慢地一个个地找这个键,而是用了一种被称为hash code的特殊值来进行查找的。散列(hash)时一种算法,它会从目标对象当中提取一些信息,然后生成一个表示这个对象的“相对独特”的int。 hashCode()是Object根类的方法,因此所有Java对象都能生成hash code。HashMap则利用对象的hashCode()来进行快速的查找。这样性能就有了急剧的提高。

Map(接口):维持键--值的关系(既pairs),这样就能用键来找值了。

HashMap*:基于hash表的实现。(用它来代替Hashtable。)提供时间恒定的插入与查询。在构造函数种可以设置hash表的capacity和load factor。可以通过构造函数来调节其性能。

LinkedHashMap(JDK 1.4):很像HashMap,但是用Iterator进行遍历的时候,它会按插入顺序或最先使用的顺序(least-recently-used (LRU)order)进行访问。除了用Iterator外,其他情况下,只是比HashMap稍慢一点。用Iterator的情况下,由于是使用链表来保存内部顺序,因此速度会更快。

TreeMap:基于红黑树数据结构的实现。当你查看键或pair时,会发现它们时按顺序(根据Comparable或Comparator,我们过一会讲)排列的。TreeMap的特点时,你锁得到的时一个有序的Map。TreeMap是Map中唯一有subMap()方法的实现。这个方法能让你获取这个树中的一部分。

WeakHashMap:一个weak key的Map,是为某些特殊问题而设计的。它能让Map释放其所持有的对象。如果某个对象除了在Map当中充当键之外,在其他地方都没有其reference的话,那它将被当作垃圾回收。

IdentityHashMap(JDK 1.4):一个用==,而不是equals()来比较键的hash map。不是为我们平常使用而设计的,是用来解决特殊问题的。

散列是往Map里存数据的常用算法。

SortedMap

SortedMap(只有TreeMap这一个实现)的键肯定是有序的,因此这个接口里面就有一些附加功能的方法了。

Comparator comparator():返回Map所使用的comparator,如果是用Object内置的方法的话,则返回null。

Object firstKey():返回第一个键。

Object lastKey():返回最后一个键。

SortedMap subMap(fromKey, toKey):返回这个Map的一个子集,其键从fromKey开始到toKey为止,包括前者,不包括后者。

SortedMap headMap(toKey):返回这个Map的一愕嘎子集,其键均小于toKey。

SortedMap tailMap(fromKey):返回这个Map的一个子集,其键均大于等于fromKey。

pair是按key的顺序存储的,由于TreeMap有顺序的概念,因此“位置”是有意义的,所以你可以去获取它的第一个和最后一个元素,以及它的子集。

LinkedHashMap

为了提高速度,LinkedHashMap对所有东西都做了hash,而且遍历的时候(println()会遍历整个Map,所以你能看到这个过程)还会按插入顺序返回pair。此外,你还可以在LinkedHashMap的构造函数里面进行配置,让它使用基于访问的LRU(least-recently -used)算法,这样还没被访问过的元素(同时也是要删除的候选对象)就会出现在队列的最前头。这样,为节省资源而写一个定时清理的程序就变得很简单了。

5.总结Java标准类库的容器类:

1)数组把对象和数字形式的下标联系起来。它持有的是类型确定的对象,这样提取对象的时候就不用再作类型传递了。它可以是多维的,也可以持有primitive。但是创建之后它的容量不能改了。

2)Collection持有单个元素,而Map持有相关联的pair。

3)和数组一样,List也把数字下标同对象联系起来,你可以把数组和List想成有序的容器。List会随元素的增加自动调整容量。但是List只能持有Object reference,所以不能存放primitive,而且把Object提取出来之后,还要做类型传递。

4)如果要做很多随机访问,那么请用ArrayList,但是如果要再List的中间做很多插入和删除的话,就应该用LinkedList了。

5)LinkedList能提供队列,双向队列和栈的功能。

6)Map提供的不是对象与数组的关联,而是对象和对象的关联。

HashMap看重的是访问速度,而TreeMap各国那看重键的顺序,因而它不如HashMap那么快。而LinkedHashMap则保持对象插入的顺序,但是也可以用LRU算法为它重新排序。

7)Set只接受不重复的对象。HashSet提供了最快的查询速度。而TreeSet则保持元素有序。LinkedHashSet保持元素的插入顺序。

8)没必要再在新代码里使用旧类库留下来的Vector,Hashtable和Stack了。

容器类库是你每天都会用到的工具,它能使程序更简介,更强大并且更搞笑。

6.理解hashCode()

如果你不覆写键的hashCode()和equals()的话,散列数据结构(HashSet,HashMap,LinkedHashSet,或LinkedHashMap)就没法正确地处理键。

散列的价值就在于速度:散列算法能很快地找出东西。

数组是最快的数据结构。

7.Collection 和 Map 的区别

容器内每个为之所存储的元素个数不同。

Collection类型者,每个位置只有一个元素。

Map类型者,持有 key-value pair,像个小型数据库。

8. 其他特征

* List,Set,Map将持有对象一律视为Object型别。

* Collection、List、Set、Map都是接口,不能实例化。

继承自它们的 ArrayList, Vector, HashTable, HashMap是具象class,这些才可被实例化。

* vector容器确切知道它所持有的对象隶属什么型别。vector不进行边界检查。

9. 容器类和Array的区别、择取

* 容器类仅能持有对象引用(指向对象的指针),而不是将对象信息copy一份至数列某位置。

* 一旦将对象置入容器内,便损失了该对象的型别信息。

10. 、* 在各种Lists中,最好的做法是以ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList();Vector总是比ArrayList慢,所以要尽量避免使用。

* 在各种Sets中,HashSet通常优于HashTree(插入、查找)。只有当需要产生一个经过排序的序列,才用TreeSet。

HashTree存在的唯一理由:能够维护其内元素的排序状态。

* 在各种Maps中:HashMap用于快速查找。

* 当元素个数固定,用Array,因为Array效率是最高的。

结论:最常用的是ArrayList,HashSet,HashMap,Array。

11. 注意:

1)Collection没有get()方法来取得某个元素。只能通过iterator()遍历元素。

2)Set和Collection拥有一模一样的接口。

3)List,可以通过get()方法来一次取出一个元素。使用数字来选择一堆对象中的一个,get(0)...。(add/get)

4)一般使用ArrayList。用LinkedList构造堆栈stack、队列queue。

5)Map用 put(k,v) / get(k),还可以使用containsKey()/containsValue()来检查其中是否含有某个key/value。

HashMap会利用对象的hashCode来快速找到key。

* hashing

哈希码就是将对象的信息经过一些转变形成一个独一无二的int值,这个值存储在一个array中。

我们都知道所有存储结构中,array查找速度是最快的。所以,可以加速查找。 发生碰撞时,让array指向多个values。即,数组每个位置上又生成一个梿表。

6)Map中元素,可以将key序列、value序列单独抽取出来。

使用keySet()抽取key序列,将map中的所有keys生成一个Set。

使用values()抽取value序列,将map中的所有values生成一个Collection。

为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复。