实现Map接口的各集合讨论

来源:互联网 发布:php 数组按字段值排序 编辑:程序博客网 时间:2024/06/11 16:58
  • HashMap集合
  • LinkedHashMap集合
  • TreeMap集合
  • HashTable集合
  • ConcurrentHashMap

HashMap集合

  • 概念

    public class HashMap extends AbstractMap implements Map

    • HashMap集合继承Dictionary 类,是一个数组和链表的结合体(在数据结构称“链表散列“),非线程安全。null可以作为键,但这样的键只有一个;可以有一个或多个键所对应的值为null。不能保证插入顺序

    • HashMap中hash数组的默认大小是16,而且一定是2的指数。每次都以1倍的量扩容。

  • 实现机制

    • HashMap集合的put()方法实现机制:

      将一个 key-value 对放入 HashMap 中时,程序首先根据该 key 的 hashCode() 返回值决定该 Entry 的存储位置(bucket桶位置):如果两个 Entry 的 key 的 hashCode() 返回值相同,那它们的存储位置相同。如果这两个 Entry 的 key 通过 equals 比较返回 true,新添加 Entry 的 value 将覆盖集合中原有 Entry 的 value,但 key 不会覆盖。如果这两个 Entry 的 key 通过 equals 比较返回 false,新添加的 Entry 将与集合中原有 Entry 形成 Entry 链,而且新添加的 Entry 位于 Entry 链的头部。

    • HashMap集合的get()方法实现机制:

      当 HashMap 的每个 bucket 里存储的 Entry 只是单个 Entry ——也就是没有通过指针产生 Entry 链时,只要先计算出该 key 的 hashCode() 返回值,在根据该 hashCode 返回值找出该 key 在bucket数组中的索引,然后取出该索引处的 Entry,最后返回该 key 对应的 value 即可。
      在发生“Hash 冲突”的情况下,单个 bucket 里存储的不是一个 Entry,而是一个 Entry 链,系统只能必须按顺序遍历每个 Entry,直到找到想搜索的 Entry 为止。

LinkedHashMap集合

  • 概念
    • LinkedHashMap集合是HashMap的一个子类,底层使用哈希表与双向链表来保存所有元素,具有可预知的迭代顺序,允许使用null值和null键。根据链表中元素的顺序可以分为:按插入顺序的链表(默认),和按访问顺序(调用get方法)的链表
    • 如果需要输出的顺序和输入时的相同,那么就选用LinkedHashMap。

TreeMap集合

  • 概念

    TreeMap的底层是通过红黑树算法的实现,具有对数据对象的排序功,如果希望输出的顺序是升序的,可以选择TreeMap集合。不允许空键值

  • 实现机制

    • 在TreeMap的put()的实现方法中主要分为两个步骤,第一:构建排序二叉树,第二:平衡二叉树。

      对于排序二叉树的创建,其添加节点的过程如下:
      1、以根节点为初始节点进行检索。
      2、与当前节点进行比对,若新增节点值较大,则以当前节点的右子节点作为新的当前节点。否则以当前节点的左子节点作为新的当前节点。
      3、循环递归2步骤知道检索出合适的叶子节点为止。
      4、将新增节点与3步骤中找到的节点进行比对,如果新增节点较大,则添加为右子节点;否则添加为左子节点。

    • TreeMap deleteEntry()方法实现分析

      找到一个替代子节点C来替代P,然后直接删除C,最后调整这棵红黑树。

HashTable集合

  • 概念

    public class Hashtable extends Dictionary implements Map

    • HashTable采用”拉链法”实现哈希表,它定义了几个重要的参数:table、count、threshold、loadFactor、modCount。
      • table:为一个Entry[]数组类型,Entry代表了“拉链”的节点,每一个Entry代表了一个键值对,哈希表的”key-value键值对”都是存储在Entry数组中的。是一个volatile变量。
      • count:HashTable的大小,注意这个大小并不是HashTable的容器大小,而是他所包含Entry键值对的数量。是一个volatile变量。
      • threshold:Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值=”容量*加载因子”。
      • loadFactor:加载因子。
      • modCount:用来实现“fail-fast”机制的(也就是快速失败)。所谓快速失败就是在并发集合中,其进行迭代操作时,若有其他线程对其进行结构性的修改,这时迭代器会立马感知到,并且立即抛出ConcurrentModificationException异常,而不是等到迭代完成之后才告诉你(你已经出错了)。
    • HashTable中hash数组默认大小是11,增加的方式是 old*2+1。
    • HashTable不允许空键值。
    • HashTable是同步,线程安全的,每次执行,通过synchronized,锁定整张Hash表的,即每次锁住整张表让线程独占。
    • 实现机制
      • put()方法和get()与HashMap基本一致,只是在扩容上有区别(上面已经提到)

ConcurrentHashMap

  • 可以说ConcurrentHashMap是在优化HashTable线程同步性能基础上产生的,HashTable容器效率低下是因为所有访问HashTable的线程都必须竞争同一把锁,那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap。
  • ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。不允许null key和null value
    • ConcurrentHashMap中主要实体类就是三个:ConcurrentHashMap(整个Hash表),Segment(子Hash表),HashEntry(节点)。
    • ConcurrentHashMap完全允许多个读操作并发进行,读操作并不需要加锁。实现技术是保证HashEntry几乎是不可变的,final类型。除了value不是final的,其它值都是final的,这意味着不能从hash链的中间或尾部添加或删除节点,因为这需要修改next 引用值,所有的节点的修改只能从头部开始。对于put操作,可以一律添加到Hash链的头部。但是对于remove操作,可能需要从中间删除一个节点,这就需要将要删除节点的前面所有节点整个复制一遍,最后一个节点指向要删除结点的下一个结点。为了确保读操作能够看到最新的值,将value设置成volatile,这避免了加锁。
  • 实现机制

    • 删除操作 remove()

      整个操作是在持有段锁的情况下执行的,首先定位到要删除的节点e。接下来,如果不存在这个节点就直接返回null,否则就要将e前面的结点复制一遍,尾结点指向e的下一个结点。e后面的结点不需要复制,它们可以重用。

      • 整个操作是先定位到段,然后委托给段的remove操作。当多个删除操作并发进行时,只要它们所在的段不相同,它们就可以同时进行
        -注:1、当要删除的结点存在时,删除的最后一步操作要将count的值减一
        2、remove执行的开始就将table赋给一个局部变量tab。
    • 添加操作 put()

      持有段锁(锁定整个segment)的情况下执行的。首先找是否存在同样一个key的结点,如果存在就直接替换这个结点的值。否则创建一个新的结点并添加到hash链的头部,这时一定要修改modCount和count的值,同样修改count的值一定要放在最后一步。

    • 获取操作 get()

      get操作不需要锁。根据hash和key对hash链进行遍历找到要获取的结点,如果没有找到,直接访回null。

0 0
原创粉丝点击