Java 集合:HashSet,HashTable,以及 ConcurrentHashMap 概念

转载自https://github.com/pzxwhc/MineKnowContainer/issues/21

HashSet

阅读本文之前建议先弄懂 HashMap:Java 集合:HashMap(put方法的实现 与 哈希冲突),因为无论是 HashSet 还是 HashTable 本质上来说还是基于 HashMap的。

HashSet 实现了 Set 接口,而 Set 接口又继承于 Collection 接口。 它可以用于存储数据。

Collection 是 Java 集合的一个根接口,JDK 没有它的实现类。 内部仅仅做 add(),remove(),contains(),size() 等方法的声明。Set 接口不可以有重复的值,但是可以有空值。并且没有做同步处理。

HashSet 的实现就是基于 HashMap。HashSet 的很多方法就是调用 HashMap 的对应的方法

我们知道,HashMap 是键值对的形式,HashSet 没有什么键值对的概念,那它内部具体又是怎么实现的呢?

其实就是 HashSet 用了一个空对象,如 private static final Object PRESENT = new Object();

  • 用这个空对象来填充 HashMap 的 value 域
  • 用这个空对象来填充 HashMap 的 value 域
  • 用这个空对象来填充 HashMap 的 value 域

如下面的 add 方法:

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

所以从这里就可以看出:

  1. 利用了 HashMap 实现。HashSet 的方法就是调用 HashMap 的对应的方法。
  2. 用空对象来填充 HashMap 的 value 域

HashTable

HashTable 是 HashMap 的线程安全版本。 内部的实现几乎和 HashMap 一模一样。例如:

同样的有一个数组:

private transient Entry<?,?>[] table;

对于 put 方法:

public synchronized V put(K key, V value) {
    ......

    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    for(; entry != null ; entry = entry.next) {
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }

    addEntry(hash, key, value, index);
    return null;
}

这里可以看到, for 循环表示如果出现了哈希冲突,那么就放在最后一位。因为不断的进行 entry = entry.next,直到 entry != null需要注意的是,JDK8 中的 HashMap 如果有很多哈希冲突的话,那么是可能会把链表变成红黑树以此来提高效率。但是这里 HashTable 并没有这样做。

另外,从这里也可以看出,HashTable 实现多线程同步的主要方式是通过加 synchronized 关键字。

另外,对于 get 方法:

@SuppressWarnings("unchecked")
public synchronized V get(Object key) {
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            return (V)e.value;
        }
    }
    return null;
}

这里最明显的就是 synchronized,其实还有很多其他的方法用的也是 synchronized。get 方法的处理也是先根据 key 定位到 table 的某一个位置,最后再 for 循环拿到该值(因为可能出现了哈希冲突,所以要 for 循环)。

ConcurrentHashMap

对于 ConcurrentHashMap 并没有深入研究过源码。但是了解一些如下的概念:

  1. ConcurrentHashMap 也是 HashMap 的线程安全版本,和 HashTable 一样。它支持完全并发的读和一定程度的并发的写。所以说会比 HashTable 的性能要高出很多。

  2. 实现:ConcurrentHashMap允许多个修改操作并发的进行,关键在于使用了锁分离的技术。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。但是有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而不仅仅是某个段。

  3. HashTable 和 HashMap 最主要的区别:HashTable 每次执行同步的时候都是需要锁住整个结构。ConcurrentHashMap 正是为了解决这个问题而诞生的。ConcurrentHashMap 默认会把 hash 表分成16个桶。比如说 get,put,remove等操作就是锁住当前自己的桶。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值