Java常用集合使用

常用的集合类型有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型的索引

 

SetMap
EnumSetEnumMap
SortedSetSortedMap
TreeSetTreeMap
NavigableSetNavigableMap
HashSetHashMap
LinkedHashSetLinkedHashMap

 

二、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:

基于 TreeMap NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 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。。。。。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值