Java 集合与泛型

Java集合框架与泛型

首先整体概览,下图是Java集合框架的类图    




高清类图的PDF下载地址:http://download.csdn.net/detail/mydream20130314/8574911




简单点说,List保存对象列表,Set保存无重复对象列表,Map保存键值对映射.

下面逐个简要介绍 :


ArrayDeque<E>

  • 底层数据结构

    列表
    双端队列

  • 说明

    该类实现的双端队列没有容量限制,或者为其指定容量

    该类返回的迭代器是快速失败

    该类不是线程安全的 , 不支持并发访问

    此类很可能在用作堆栈时快于 Stack,在用作队列时快于 LinkedList

    大多数操作以固定时间运行,异常包括 remove、removeFirstOccurrence、removeLastOccurrence、contains、iterator.remove() 以及批量操作,它们均以线性时间运行

  • 版本

    since 1.6

PriorityQueue<E>

  • 底层数据结构

    一个基于优先级堆的无界优先级队列
    最小堆

  • 说明

    该优先级队列默认按照自然顺序排序,元素必须是可比较的,且不为null

    可以为其提供一个Comparator进行排序

    该队列的容量可以随元素添加自动扩充,或者为其指定一个容量

    该类的迭代器不保证元素有序

    该类不是线程安全的,非同步集合.相反,有线程安全的 PriorityBlockingQueue 类

    该类实现为排队和出队方法(offer、poll、remove() 和 add)提供 O(log(n)) 时间; 为 remove(Object) 和 contains(Object) 方法提供线性时间; 为获取方法(peek、element 和 size)提供固定时间

  • 版本

    since 1.5

ArrayList<E>

  • 底层数据结构

    列表

  • 说明

    该类可以随机访问元素,容量自动扩充

    添加和删除操作以线性时间运行

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

    该类不是线程安全的

    该类的迭代器是快速失败

  • 版本

    since 1.2

LinkedList<E>

  • 底层数据结构

    链表

  • 说明

    该类可以用作堆栈,队列或者双端队列

    索引操作将是从链头或者链尾遍历

    该类不是线程安全的,迭代器是快速失败

    在大量的插入,删除操作时使用有较高的性能

  • 版本

    since 1.2

Stack<E>

  • 底层数据结构

    列表
    LIFO堆栈

  • 说明

    该类对Vector进行了扩展,提供了LIFO堆栈的基本操作

    Deque 接口及其实现提供了 LIFO 堆栈操作的更完整和更一致的 set,应该优先使用此 set,而非此类。例如:Deque stack = new ArrayDeque();

    早期的堆栈实现,如今,LinkedList,Deque等都提供了堆栈操作

  • 版本

    since 1.0

Vector<E>

  • 底层数据结构

    列表

  • 说明

    该类是可以自动扩充或缩减的对象数组

    该类的迭代器是快速失败

    从 Java 2 平台 v1.2 开始,此类改进为可以实现 List 接口,使它成为 Java Collections Framework 的成员。与新 collection 实现不同,Vector 是同步的

EnumSet<E extends Enum<E>>

  • 底层数据结构

    HASH列表

  • 说明

    与枚举类型一起使用的专用 Set 实现
    由 iterator 方法返回的迭代器按其自然顺序 遍历这些元素(该顺序是声明枚举常量的顺序)。返回的迭代器是弱一致 的,将不会对set的修改返回任何影响
    迭代器不是线程安全的.
    所有的操作都以固定时间运行

  • 版本

    since 1.5

EnumMap<E extends Enum<E>,V>

  • 底层数据结构

    HASH列表

  • 说明

    与枚举类型一起使用的专用 Map 实现
    枚举映射根据其键的自然顺序 来维护(该顺序是声明枚举常量的顺序)

    返回的迭代器是弱一致 的,将不会对set的修改返回任何影响
    不是线程安全的.
    所有的操作都以固定时间运行

  • 版本

    since 1.5

HashSet<E>

  • 底层数据结构

    hash表

  • 说明

    仅允许一个元素为null
    此类实际上由HashMap实例支持,只不过没有使用Map的Key.
    该类的迭代顺序是不确定的
    该类不是线程安全的
    该类的迭代器的快速失败行为无法得到保证

  • 版本

    since 1.5

HashMap<K,V>

  • 底层数据结构

    hash表

  • 说明

    该类是基于hash表的Map实现.提供可选的映射操作.
    可以使用null键和null值 该类不是是线程安全的,迭代器是快速失败的. 文后有关于Hash表的介绍 后文有HashMap与Hashtable的比较

  • 版本

    since 1.5

LinkedHashSet<E>

  • 底层数据结构

    hash表

  • 说明

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

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

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

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

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

  • 版本

    since 1.4

LinkedHashMap<K,V>

  • 底层数据结构

    hash表

  • 说明
    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 缓存。调用 put 或 get 方法将会访问相应的条目(假定调用完成后它还存在)。putAll 方法以指定映射的条目集迭代器提供的键-值映射关系的顺序,为指定映射的每个映射关系生成一个条目访问。任何其他方法均不生成条目访问。特别是,collection 视图上的操作不 影响底层映射的迭代顺序。

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

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

  • 版本

    since 1.4

TreeSet<E>

  • 底层数据结构

    Red-Black tree(红黑树)

  • 说明

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

    此实现为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销

  • 版本

    since 1.2

TreeMap<K,V>

  • 底层数据结构

    Red-Black tree(红黑树)

  • 说明

    基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序
    此实现为 containsKey、get、put 和 remove 操作提供受保证的 log(n) 时间开销
    该类不是线程同步的,迭代器是快速失败的
    通过put(k,v)可以更改键值映射

  • 版本

    since 1.2

Hashtable<K,V>

  • 底层数据结构

    HASH表

  • 说明

    早期的Map实现,元素都不可为null
    默认加载因子(.75)
    返回的迭代器是快速失败的
    由 Hashtable 的键和元素方法返回的 Enumeration 不是快速失败的
    该类不是线程安全的
    它与HashMap最大的区别就是HashMap允许键或值为空,而Hashtable均不可为null
    文后有关于Hash表的介绍 后文有HashMap与Hashtable的比较

  • 版本

    since 1.0

Fail-safe和iterator迭代器相关

如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。

结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构

Hash表

  • Hash表是除顺序表,链表,索引表之外的又一种线性表存储结构,几乎可以在常数时间内通过键获取值.

  • Hash表有两个因素决定其性能:初始容量 和加载因子。容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构,重新计算哈希值寻址),从而将哈希表扩充大约为原来的两倍

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

    如果有大量的映射需要存储在Map实例中,设置一个较高的初始容量,相对于自动rehash来说会更好.

  • hash算法:

    一个将键值映射为内存地址的算法 优良的hash算法可以减少"哈希冲突"的发生

  • 哈希冲突:

    多个键值计算后的出的地址值相同即为哈希冲突.
    在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索

HashMap和Hashtable的区别

HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。

  • HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。

  • HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

  • 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。

  • 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。

HashSet和HashMap的区别

  • HashMap实现了Map接口,HashSet实现了Set接口

  • HashMap储存键值对 HashSet仅仅存储对象

  • 使用put()方法将元素放入map中 使用add()方法将元素放入set中

  • HashMap中使用键对象来计算hashcode值 HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false

  • HashMap比较快,因为是使用唯一的键来获取对象 HashSet较HashMap来说比较慢

  • HashSet 本质上就是HashMap,他只是使用了HashMap的key部分参见源码

    public HashSet() {
        map = new HashMap<E,Object>();
    }
    

集合工具类 Collections

Collections提供了大量的静态工具,操作集合,例如稳定排序,查找,搜索,提取等

使用同步集合

Collection提供多个方法将普通集合包装成线程同步的集合.synchronizedXxxx()
例如:Map m = Collections.synchronizeMap(hashMap);

如果使用JDK1.5及以上的话,可以考虑使用Java包java.util.concurrent 中的已Concurrent开头的集合工具


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值