HashTable/HashMap/ConcurrentHashMap

本文详细介绍了Hashtable的基本概念、构造函数、核心变量及其工作原理,对比了Hashtable与HashMap的主要区别,并阐述了ConcurrentHashMap的锁分离技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述:

     Hashtable 是散列表,存储的是键值对(key-value)的映射。Hashtable 继承与Dictionary,实现了Map、Cloneable、Serializable接口。它是线程安全的。Hashtable 中的映射不是有序的。
     Hashtable 有四个构造函数,分别是:

// 默认构造函数。
public Hashtable()

// 指定“容量大小”的构造函数 
public Hashtable(int initialCapacity)

// 指定“容量大小”和“加载因子”的构造函数
public Hashtable(int initialCapacity, float loadFactor)

// 包含“子Map”的构造函数
public Hashtable(Map<? extends K, ? extends V> t)
   

重点解析:

      HashTable是通过“拉链法”解决冲突的。HashTable中的几个重要变量:

private transient HashtableEntry<K,V>[] table;
private transient int count;
private float loadFactor;
private int threshold;
private transient int modCount = 0;
      ①、table是HashtableEntry数组,HashtableEntry是实现了Map、Entry接口的单链表,其内部有对自己另一个实例的引用next。②、count指示了当前Hashtable 中Entry的数量。③、loadFactor是加载因子,是Hashtable 在其容量自动增加之前可以达到多满的一个度。默认值是0.75,是时间和空间寻求的折中。④、threshold是Hashtable进行rehash的一个临界值,其值为(capacity * loadFactor)。⑤、modCount是Hashtable 被结构性更改的次数(指的是改变entries的数量或rehash)

    
     主要看下put方法和get方法:
     put:
public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
        throw new NullPointerException();
    }

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

    modCount++;
    if (count >= threshold) {
        // Rehash the table if the threshold is exceeded
        rehash();

        tab = table;
        hash = hash(key);
        index = (hash & 0x7FFFFFFF) % tab.length;
    }

    // Creates the new entry.
    HashtableEntry<K,V> e = tab[index];
    tab[index] = new HashtableEntry<>(hash, key, value, e);
    count++;
    return null;
}
     put方法前使用了syncronized修饰,说明Hashtable是线程安全的。传入key和value,首先取得key的hashcode(key.hashCode()),然后先于0x7FFFFFFF与运算(网上说法是为了避免hash是负数),然后与链表数组的长度(即HashtableEntry[] tab)进行求余,得到在链表数组中的index。然后判断在tab[index]这条链中是否存在哈希码与键都相同的,如果存在则替换并返回旧的值。否则构造一个新的HashtableEntry作为table[index]的头结点,并把原来的tab[index]作为插入对象的下一个结点,返回null。

     get:
public synchronized V get(Object key) {
    HashtableEntry tab[] = table;
    int hash = hash(key);
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (HashtableEntry<K,V> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            return e.value;
        }
    }
    return null;
}
     get方法更容易点。还是先获取哈希码,计算index。然后依次查找是否存在哈希码与key都匹配的,若存在就返回。


与HashMap的区别

     可以将HashMap看成是Hashtable的轻量级实现。他们都完成了Map接口。
     主要区别在于
          ①、HashMap允许键值为null(允许最多一条记录的键为null,允许多条记录的值为null),而Hashtable不允许。
          ②、且HashMap是线程不安全的,效率上可能高于Hashtable,而Hashtable是线程安全的(方法由synchronized修饰)。


ConcurrentHashMap
     Hashtable是线程安全的,synchronized是针对整张表,即每次锁住整张表让线程独占,ConcurrentHashMap则允许多个修改并发进行,其关键在于使用了锁分离技术,使用多个锁来控制对hash表不同部分进行的修改。
      ConcurrentHashMap内部使用段(Segment)来表示不同的部分,每个段其实就是一个小的hash表,它们有自己的锁,只要多个修改发生在不同的段上,它们就可以并发进行。
     针对有些需要跨段的方法,比如size()、containsValue()等,需要锁定的是整张表,因此需要按顺序锁定所有的段,操作完毕后再按顺序释放所有的段。这里的顺序很重要,否则可能发生死锁。

     ConcurrentHashMap中的读操作不用加锁,完全允许多个读操作并发进行。

HashTableHashMapConcurrentHashMap 都是 Java 中用于存储键值对的数据结构,它们的主要区别在于线程安全性和性能。 1. HashTable:在早期的 Java 版本中引入,是一个线程安全的哈希表实现。它使用 synchronized 关键字来保证对内部数据结构的访问是线程安全的。然而,由于线程间的同步开销比较大,在并发环境下性能较差。此外,HashTable 不允许存储 null 键或 null 值。 2. HashMap:是 Java Collections Framework 中的一部分,是 HashTable 的非线程安全实现。相比于 HashTableHashMap 的性能更好,因为它没有进行同步操作。然而,这也意味着在多线程环境下使用 HashMap 时需要进行额外的同步措施,以保证线程安全。与 HashTable 类似,HashMap 也不允许存储 null 键,但可以存储 null 值。 3. ConcurrentHashMap:是 Java 5 引入的线程安全哈希表实现。相比于 HashTable 和使用同步关键字的 HashMapConcurrentHashMap 在并发场景下有更好的性能表现。它通过将数据分割成多个段(Segment)来实现并发访问的高效率。每个段相当于一个小的 HashTable,在多线程访问时只需要锁住该段,而不是整个数据结构。因此,多个线程可以同时访问 ConcurrentHashMap 的不同段,从而提高并发性能。与 HashMap 类似,ConcurrentHashMap 允许存储 null 值。 总结:如果在单线程环境中使用,HashMap 是最常用的选择;在多线程环境中,如果需要线程安全,可以选择 HashTableConcurrentHashMap。其中 ConcurrentHashMap 在高并发场景下的性能表现更好。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值