Java常用集合使用

来源:互联网 发布:软件开发甲级资质 编辑:程序博客网 时间:2024/06/06 04:14

常用的集合类型有Set系列、Map系列、List系列,下面看一下网上下载的几张类图:

 

上面这两张图基本上展示了常用集合的基本关系。

 

一、Set与Map、List的关系

1、Set代表一种集合元素无序、集合元素不可重复的集合

2、Map代表一个有多个Key-Value对组成的集合,Key不能重复,Key之间无序

3、Set与Map表面上看起来没有什么关系,但是如果将Key、Value堪称一个对象,那么Map就相当于是Set

4、List按照元素的加入先后顺序保存元素,可以用过元素的精确位置获取元素;其余用法与Map相似,其key是一个int型的索引

 

SetMapEnumSetEnumMapSortedSetSortedMapTreeSetTreeMapNavigableSetNavigableMapHashSetHashMapLinkedHashSetLinkedHashMap

 

二、HashSet与HasnMap与HashTable

HashSet与HashMap分别是Set与Map的具体实现,在实际中经常使用的类。

二者有很多相似之处,采用哈市算法决定元素存放位置,能够保证快速的提取结合元素。

集合在存储对象的时候并没有真正的将对象放入集合中,而只是保留对该对象的引用而已

 1、HashSet:

此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用null 元素。

此类为基本操作提供了稳定性能,这些基本操作包括 addremovecontainssize,假定哈希函数将这些元素正确地分布在桶中。对此 set 进行迭代所需的时间与HashSet 实例的大小(元素的数量)和底层HashMap 实例(桶的数量)的“容量”的和成比例。因此,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

注意,此实现不是同步的。

 

 public static void main(String[] args) {        HashSet hs = new HashSet(4);        hs.add("语文:80");        hs.add("数学:90");        hs.add("英语:95");        hs.add(null);        System.out.print(hs);    }[null, 数学:90, 语文:80, 英语:95]成功构建 (总时间: 0 秒)


2、HashMap:

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(getput)提供稳定的性能。迭代 collection 视图所需的时间与HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

HashMap 的实例有两个参数影响其性能:初始容量加载因子容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。

通常,默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括getput 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。

如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。

注意,此实现不是同步的


public static void main(String[] args) {        HashMap<String, Double> hm = new HashMap<String, Double>();        hm.put("语文", Double.valueOf(80));        hm.put("数学", Double.valueOf(90));        hm.put("英语", Double.valueOf(95));                System.out.print(hm);    }{语文=80.0, 英语=95.0, 数学=90.0}成功构建 (总时间: 0 秒)


 


 3、HashTable:

此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。

为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。

Hashtable 的实例有两个参数影响其性能:初始容量加载因子容量 是哈希表中 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子 是对哈希表在其容量自动增加之前可以达到多满的一个尺度。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。

通常,默认加载因子(.75)在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数 Hashtable 操作中,包括getput 操作,都反映了这一点)。

初始容量主要控制空间消耗与执行 rehash 操作所需要的时间损耗之间的平衡。如果初始容量大于 Hashtable 所包含的最大条目数除以加载因子,则永远 不会发生rehash 操作。但是,将初始容量设置太高可能会浪费空间。

如果很多条目要存储在一个 Hashtable 中,那么与根据需要执行自动 rehashing 操作来增大表的容量的做法相比,使用足够大的初始容量创建哈希表或许可以更有效地插入条目。

注意,此实现是同步的

 

 public static void main(String[] args) {        Hashtable<String, Double> ht = new Hashtable<String, Double>();        ht.put("语文", Double.valueOf(80));        ht.put("数学", Double.valueOf(90));        ht.put("英语", Double.valueOf(95));                System.out.print(ht);    }{语文=80.0, 英语=95.0, 数学=90.0}成功构建 (总时间: 0 秒)


 三、TreeSet与TreeMap

1、TreeMap:

基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的Comparator 进行排序,具体取决于使用的构造方法。

此实现为 containsKeygetputremove 操作提供受保证的 log(n) 时间开销。这些算法是 Cormen、Leiserson 和 Rivest 的Introduction to Algorithms 中的算法的改编。

注意,如果要正确实现 Map 接口,则有序映射所保持的顺序(无论是否明确提供了比较器)都必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅ComparableComparator)。这是因为Map 接口是按照 equals 操作定义的,但有序映射使用它的compareTo(或compare)方法对所有键进行比较,因此从有序映射的观点来看,此方法认为相等的两个键就是相等的。即使排序与 equals 不一致,有序映射的行为仍然 定义良好的,只不过没有遵守Map 接口的常规协定。

注意,此实现不是同步的

 

public static void main(String[] args) {        TreeMap<String, Double> tm = new TreeMap<String, Double>();        tm.put("语文", Double.valueOf(80));        tm.put("数学", Double.valueOf(90));        tm.put("英语", Double.valueOf(95));                System.out.print(tm);    }{数学=90.0, 英语=95.0, 语文=80.0}成功构建 (总时间: 0 秒)


 

2、TreeSet:

基于 TreeMapNavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的Comparator 进行排序,具体取决于使用的构造方法。

此实现为基本操作(addremovecontains)提供受保证的 log(n) 时间开销。

注意,如果要正确实现 Set 接口,则 set 维护的顺序(无论是否提供了显式比较器)必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅ComparableComparator。)这是因为Set 接口是按照equals 操作定义的,但TreeSet 实例使用它的 compareTo(或 compare)方法对所有元素进行比较,因此从 set 的观点来看,此方法认为相等的两个元素就是相等的。即使 set 的顺序与 equals 不一致,其行为也 定义良好的;它只是违背了Set 接口的常规协定。

注意,此实现不是同步的

 

 public static void main(String[] args) {        TreeSet ts = new TreeSet();        ts.add("语文:85");        ts.add("数学:95");        ts.add("英语:90");        System.out.print(ts);    }[数学:95, 英语:90, 语文:85]成功构建 (总时间: 0 秒)

 

四、LinkedHashSet与LinkedHashMap与LinkedList

1、LinkedHashSet:

有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。注意,插入顺序 受在 set 中重新插入的 元素的影响。(如果在s.contains(e) 返回true 后立即调用s.add(e),则元素e 会被重新插入到 sets 中。)

此实现可以让客户免遭未指定的、由 HashSet 提供的通常杂乱无章的排序工作,而又不致引起与TreeSet 关联的成本增加。使用它可以生成一个与原来顺序相同的 set 副本,并且与原 set 的实现无关:

     void foo(Set s) {         Set copy = new LinkedHashSet(s);         ...     } 

如果模块通过输入得到一个 set,复制这个 set,然后返回由此副本决定了顺序的结果,这种情况下这项技术特别有用。(客户通常期望内容返回的顺序与它们出现的顺序相同。)

此类提供所有可选的 Set 操作,并且允许 null 元素。与 HashSet 一样,它可以为基本操作(addcontainsremove)提供稳定的性能,假定哈希函数将元素正确地分布到存储段中。由于增加了维护链接列表的开支,其性能很可能会比HashSet 稍逊一筹,不过,这一点例外:LinkedHashSet 迭代所需时间与 set 的大小 成正比,而与容量无关。HashSet 迭代很可能支出较大,因为它所需迭代时间与其容量 成正比。

链接的哈希 set 有两个影响其性能的参数:初始容量加载因子。它们与 HashSet 中的定义极其相同。注意,为初始容量选择非常高的值对此类的影响比对HashSet 要小,因为此类的迭代时间不受容量的影响。

注意,此实现不是同步的。

public static void main(String[] args) {        LinkedHashSet lhs = new LinkedHashSet();        lhs.add("语文:85");        lhs.add("数学:95");        lhs.add("英语:90");        System.out.print(lhs);    }[语文:85, 数学:95, 英语:90]成功构建 (总时间: 0 秒)


2、LinkedHashMap

Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。注意,如果在映射中重新插入 键,则插入顺序不受影响。(如果在调用m.put(k, v)m.containsKey(k) 返回了true,则调用时会将键k 重新插入到映射m 中。)

此实现可以让客户避免未指定的、由 HashMap(及 Hashtable)所提供的通常为杂乱无章的排序工作,同时无需增加与 TreeMap 相关的成本。使用它可以生成一个与原来顺序相同的映射副本,而与原映射的实现无关:

     void foo(Map m) {         Map copy = new LinkedHashMap(m);         ...     } 

如果模块通过输入得到一个映射,复制这个映射,然后返回由此副本确定其顺序的结果,这种情况下这项技术特别有用。(客户通常期望返回的内容与其出现的顺序相同。)

提供特殊的构造方法来创建链接哈希映射,该哈希映射的迭代顺序就是最后访问其条目的顺序,从近期访问最少到近期访问最多的顺序(访问顺序)。这种映射很适合构建 LRU 缓存。调用putget 方法将会访问相应的条目(假定调用完成后它还存在)。putAll 方法以指定映射的条目集迭代器提供的键-值映射关系的顺序,为指定映射的每个映射关系生成一个条目访问。任何其他方法均不生成条目访问。特别是,collection 视图上的操作 影响底层映射的迭代顺序。

可以重写 removeEldestEntry(Map.Entry) 方法来实施策略,以便在将新映射关系添加到映射时自动移除旧的映射关系。

此类提供所有可选的 Map 操作,并且允许 null 元素。与 HashMap 一样,它可以为基本操作(addcontainsremove)提供稳定的性能,假定哈希函数将元素正确分布到桶中。由于增加了维护链接列表的开支,其性能很可能比HashMap 稍逊一筹,不过这一点例外:LinkedHashMap 的 collection 视图迭代所需时间与映射的大小 成比例。HashMap 迭代时间很可能开支较大,因为它所需要的时间与其容量 成比例。

链接的哈希映射具有两个影响其性能的参数:初始容量加载因子。它们的定义与 HashMap 极其相似。要注意,为初始容量选择非常高的值对此类的影响比对HashMap 要小,因为此类的迭代时间不受容量的影响。

注意,此实现不是同步的。

public static void main(String[] args) {        LinkedHashMap<String, Double> lhm = new LinkedHashMap<String, Double>();        lhm.put("语文", Double.valueOf(85));        lhm.put("数学", Double.valueOf(95));        lhm.put("英语", Double.valueOf(90));        System.out.print(lhm);    }{语文=85.0, 数学=95.0, 英语=90.0}成功构建 (总时间: 0 秒)


3、LinkedList

List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾getremoveinsert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。

此类实现 Deque 接口,为 addpoll 提供先进先出队列操作,以及其他堆栈和双端队列操作。

所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。

注意,此实现不是同步的。

 public static void main(String[] args) {        LinkedList ll = new LinkedList();        ll.add("语文:85");        ll.add("数学:95");        ll.add("英语:90");        System.out.print(ll);    }[语文:85, 数学:95, 英语:90]成功构建 (总时间: 0 秒)

 

五、ArrayList与LinkedList与Vector与Stack

List接口的实现类最常用的也就这几个,其中Stack是继承与Vector,在此基础上加了五个方法(用于先进后出)

1、ArrayList

List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)

size、isEmpty、get、set、iterator 和 listIterator 操作都以固定时间运行。add 操作以分摊的固定时间 运行,也就是说,添加 n 个元素需要 O(n) 时间。其他所有操作都以线性时间运行(大体上讲)。与用于 LinkedList 实现的常数因子相比,此实现的常数因子较低。

每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。

在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。

注意,此实现不是同步的
 

public static void main(String[] args) {        ArrayList al = new ArrayList(3);        al.add("语文:85");        al.add("数学:95");        al.add("英语:90");        System.out.print(al);    }[语文:85, 数学:95, 英语:90]成功构建 (总时间: 0 秒)

 

2、LinkedList

List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。

此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。

所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。

注意,此实现不是同步的

3、Vector

Vector 类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。

每个向量会试图通过维护 capacity 和 capacityIncrement 来优化存储管理。capacity 始终至少应与向量的大小相等;这个值通常比后者大些,因为随着将组件添加到向量中,其存储将按 capacityIncrement 的大小增加存储块。应用程序可以在插入大量组件前增加向量的容量;这样就减少了增加的重分配的量。

public static void main(String[] args) {        Vector  al = new Vector(3);        al.add("语文:85");        al.add("数学:95");        al.add("英语:90");        System.out.print(al);    }[语文:85, 数学:95, 英语:90]成功构建 (总时间: 0 秒)


4、Stack

Stack 类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈。它提供了通常的 pushpop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的search 方法。

 public static void main(String[] args) {        Stack  sk = new Stack();        sk.push("语文:85");        sk.push("数学:95");        sk.push("英语:90");        System.out.print(sk);    }[语文:85, 数学:95, 英语:90]成功构建 (总时间: 0 秒)

 

 ------------------------------------------------------------------------------------------------------------------------------------------------------

continue。。。。。。。。。。