Java:HashTable类小记

对 Java 中的 HashTable类,做一个微不足道的小小小小记

概述

public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
// ...
}

需要说明的是:HashTable 和 HashMap 的实现原理基本一样,差别无非是:

  1. HashTable 不允许 key 和 value 为 null;

  2. HashTable 是线程安全的。

    但是 HashTable 线程安全的策略实现代价却太大了,简单粗暴,get/put 所有相关操作都是 synchronized 的,这相当于给整个哈希表加了一把大锁,多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞,相当于将所有的操作串行化,在竞争激烈的并发场景中性能就会非常差。

  3. 扩容方式稍有不同,见后续分析

对于 HashMap 的一点记录,见:Java:HashMap类小记

相同点

对比了 HashTable 与 HashMap 的相同点,如下:

  1. 都可以用来存储键值对
  2. 底层哈希表结构查询速度都很快
  3. 内部通过单链表解决冲突问题,容量不足会自动增加
  4. 都实现了 Map 接口
  5. 都实现了 Serializable 接口,支持序列化
  6. 实现了 Cloneable 接口,可以被克隆

不同点

  1. HashTable 继承了 Dictionary 类,而 HashMap 是继承了 AbstractMap类。Dictionary 是任何可将键映射到相应值的类的抽象父类,而 AbstractMap 是基于 Map 接口的实现,它以最大限度地减少实现此接口所需的工作。

    public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    ...
    }
    public abstract class AbstractMap<K,V> implements Map<K,V> {
    ...
    } /************************************************/ public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {
    ...
    } public abstract
    class Dictionary<K,V> {
    ...
    }

    据说这是因为:历史原因

  2. 线程安全不一样:Hashtable 是线程安全的,而 HashMap 不是线程安全的,但是我们也可以通过 Collections.synchronizedMap(hashMap),使其实现同步。

    //这是Hashtable的put()方法:
    public synchronized V put(K key, V value){
    ...
    } /************************************************/ //这是HashMap的put()方法:
    public V put(K key, V value) {
    ...
    }

    从上面的源代码可以看到 Hashtable 的 put() 方法是synchronized的,而 HashMap 的 put() 方法却不是。

  3. HashMap 的 key 和 value 都允许为 null,而 Hashtable 的 key 和 value 都不允许为 null。

    HashMap 遇到 key 为 null 的时候,调用 putForNullKey 方法进行处理(JDK 8中在hash函数中做了判断),而对 value 没有处理;

    Hashtable 遇到 null,直接返回 NullPointerException

    // HashMap
    public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
    } // 在 hash(key)方法中:对于null做了特殊的处理
    static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    } /************************************************/ // Hashtable
    public synchronized V put(K key, V value) {
    // Make sure the value is not null
    // 1.值为null直接抛出异常
    if (value == null) {
    throw new NullPointerException();
    }
    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    // 由于key是null,null当然是没有hashCode()这种方法的
    // 因此直接抛出异常NullPointerException
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    // ....
    }
  4. 遍历方式的内部实现上不同:HashMap使用 Iterator 遍历,HashTable 使用Enumeration 遍历;

    据说这也是因为:历史原因

    关于 Iterator 与 Enumeration 的异同,见:Java:Iterator接口与fail-fast小记

  5. HashMap 和 HashTable 的初始化方式扩容方式不同:

    HashMap:在构造函数中不创建数组,而是在第一次put时才创建,且初始大小为16;之后每次扩充容量为原来的两倍;

    HashTable:在构造函数直接创建hashtable,且其初始大小为11,之后每次扩充2n+1

    // hashmap初始化:HashMap hashMap = new HashMap();
    // 默认构造函数
    public HashMap() {
    // 先不创建,在使用的时候再创建
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    // 其中:DEFAULT_LOAD_FACTOR = 0.75f;
    // DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    } public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
    } /************************************************/ // hashtable初始化:Hashtable hashtable = new Hashtable();
    public Hashtable() {
    // 构造函数直接创建hashtable
    this(11, 0.75f);
    } public Hashtable(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
    throw new IllegalArgumentException("Illegal Capacity: "+
    initialCapacity);
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
    throw new IllegalArgumentException("Illegal Load: "+loadFactor); if (initialCapacity==0)
    initialCapacity = 1;
    this.loadFactor = loadFactor;
    // 始化table,获得大小为initialCapacity的table数组
    table = new Entry<?,?>[initialCapacity];
    // 计算阀值
    threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    } // hastable扩容:
    public synchronized V put(K key, V value) {
    // ... // 添加元素,见下面
    addEntry(hash, key, value, index);
    return null;
    } private void addEntry(int hash, K key, V value, int index) {
    modCount++; Entry<?,?> tab[] = table;
    // 对容量进行检验,若大于阈值,则需要进行扩容操作,见下面
    if (count >= threshold) {
    // Rehash the table if the threshold is exceeded
    rehash(); tab = table;
    hash = key.hashCode();
    index = (hash & 0x7FFFFFFF) % tab.length;
    } // Creates the new entry.
    @SuppressWarnings("unchecked")
    // 添加元素
    Entry<K,V> e = (Entry<K,V>) tab[index];
    tab[index] = new Entry<>(hash, key, value, e);
    count++;
    } // rehash,扩容操作
    protected void rehash() {
    int oldCapacity = table.length;
    Entry<?,?>[] oldMap = table; // overflow-conscious code
    // 这里:新容量=旧容量 * 2 + 1
    int newCapacity = (oldCapacity << 1) + 1;
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
    if (oldCapacity == MAX_ARRAY_SIZE)
    // Keep running with MAX_ARRAY_SIZE buckets
    return;
    newCapacity = MAX_ARRAY_SIZE;
    }
    // 新建一个size = newCapacity 的HashTable
    Entry<?,?>[] newMap = new Entry<?,?>[newCapacity]; modCount++;
    // 重新计算阀值
    threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    table = newMap; for (int i = oldCapacity ; i-- > 0 ;) {
    for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
    Entry<K,V> e = old;
    old = old.next;
    int index = (e.hash & 0x7FFFFFFF) % newCapacity;
    e.next = (Entry<K,V>)newMap[index];
    newMap[index] = e;
    }
    }
    }

参考

https://blog.csdn.net/u010983881/article/details/49762595

https://mp.weixin.qq.com/s/q1r9Pno6ANUzZ9wMzA-JSg

Java:HashTable类小记的更多相关文章

  1. Java Hashtable类

    哈希表(Hashtable)是原来的java.util中的一部分,是一个字典的具体实现. 然而,Java2重新设计的哈希表,以便它也实现了​​Map接口.因此,哈希表现已集成到集合框架.它类似于Has ...

  2. Java:ConcurrentHashMap类小记-1(概述)

    Java:ConcurrentHashMap类小记-1(概述) 对 Java 中的 ConcurrentHashMap类,做一个微不足道的小小小小记,分三篇博客: Java:ConcurrentHas ...

  3. Java:ConcurrentHashMap类小记-2(JDK7)

    Java:ConcurrentHashMap类小记-2(JDK7) 对 Java 中的 ConcurrentHashMap类,做一个微不足道的小小小小记,分三篇博客: Java:ConcurrentH ...

  4. Java:TreeMap类小记

    Java:TreeMap类小记 对 Java 中的 TreeMap类,做一个微不足道的小小小小记 概述 前言:之前已经小小分析了一波 HashMap类.HashTable类.ConcurrentHas ...

  5. Java:ConcurrentHashMap类小记-3(JDK8)

    Java:ConcurrentHashMap类小记-3(JDK8) 结构说明 // 所有数据都存在table中, 只有当第一次插入时才会被加载,扩容时总是以2的倍数进行 transient volat ...

  6. Java:HashMap类小记

    Java:HashMap类小记 对 Java 中的 HashMap类,做一个微不足道的小小小小记 概述 HashMap:存储数据采用的哈希表结构,元素的存取顺序不能保证一致.由于要保证键的唯一.不重复 ...

  7. Java:LinkedHashMap类小记

    Java:LinkedHashMap类小记 对 Java 中的 LinkedHashMap类,做一个微不足道的小小小小记 概述 public class LinkedHashMap<K,V> ...

  8. Java:LinkedList类小记

    Java:LinkedList类小记 对 Java 中的 LinkedList类,做一个微不足道的小小小小记 概述 java.util.LinkedList 集合数据存储的结构是循环双向链表结构.方便 ...

  9. Java:ArrayList类小记

    Java:ArrayList类小记 对 Java 中的 ArrayList类,做一个微不足道的小小小小记 概述 java.util.ArrayList 是大小可变的数组的实现,存储在内的数据称为元素. ...

随机推荐

  1. Git 系列教程(6)- 查看 commit 提交历史

    查看提交历史 在提交了若干更新,又或者克隆了某个项目之后,如何查看提交历史 git log 官方栗子 运行下面的命令获取该项目: git clone https://github.com/scha 运 ...

  2. 知乎大佬图文并茂的epoll讲解,看不懂的去砍他

    select.poll.epoll的文章很多,自己也看过不少经典好文.不过第一次看到讲的如此通俗易懂.又图文并茂的.因此拿来分享下,供后续翻看学习. 原文链接:https://zhuanlan.zhi ...

  3. Identity用户管理入门一(框架搭建)

    理论知识微软官方文档最完整,最详细,这里只一步步的介绍如何使用,地址:https://docs.microsoft.com/zh-cn/aspnet/core/security/authenticat ...

  4. css对角渐变

    html: <div class="hh"> </div> css: .hh{ width: 500px; height: 500px; backgroun ...

  5. js 点击复制文字

    复制input里面的文字 html: <input id="content" class="form-control" type="text&q ...

  6. FastAPI 学习之路(一)fastapi--高性能web开发框架

    fastapi是高性能的web框架.他的主要特点是:- 快速编码- 减少人为bug- 直观- 简易- 具有交互式文档 - 高性能 - 基于API的开放标准 支持python 3.6版本. 安装 pip ...

  7. CodeForce-812C Sagheer and Nubian Market(二分)

    Sagheer and Nubian Market CodeForces - 812C 题意:n个货物,每个货物基础价格是ai. 当你一共购买k个货物时,每个货物的价格为a[i]+k*i. 每个货物只 ...

  8. 洛谷P1582——倒水(进制,数学)

    https://www.luogu.org/problem/show?pid=1582 题目描述 一天,CC买了N个容量可以认为是无限大的瓶子,开始时每个瓶子里有1升水.接着~~CC发现瓶子实在太多了 ...

  9. Android——ProgressBar(进度条)

    参考资料来源于菜鸟教程--学的不仅是技术,更是梦想! 学习! 1.常用属性讲解与基础实例 从官方文档,我们看到了这样一个类关系图: ProgressBar继承与View类,直接子类有AbsSeekBa ...

  10. dedecms描述文字description长度截取办法

    [field:description  function='cn_substr(@me,126)' /]