分析 java.util.Hashtable 源码
概述
基于J11,该类已经淘汰,如果使用线程安全的则用 ConcurrentHashMap ,用线程不安全的则使用 HashMap 。仅与HashMap进行比较
结构以及依赖关系
HashTable 的结构如下图

当遇到有同样 Hash 值的情况,会通过链表来解决冲突问题(链接法,通过链表解决冲突问题)。
链接法会随着冲突的增多导致查询时间越来越慢。会出现一种恶劣的情况,当散列算法特别差时;元素总数n和某个槽位数 m 中的 k 相等,如下图所示

在这种情况下,查找的时间为 $O(1+a)$ 其中 $O(1)$ 为hash
通过下图可以得知 Hashtable 与其他类的关系
direction BT
class Cloneable {
<<Interface>>
}
class Dictionary~K, V~
class Hashtable~K, V~
class Map~K, V~ {
<<Interface>>
}
class Serializable {
<<Interface>>
}
Hashtable~K, V~ ..> Cloneable
Hashtable~K, V~ --> Dictionary~K, V~
Hashtable~K, V~ ..> Map~K, V~
Hashtable~K, V~ ..> Serializable
实际上,Hashtable中的每个元素都是一个 Map.Entry<k,v> ,Entry 是 Map 的集合形式 用来遍历Map 。Hashtable实现了该接口,Hashtable就是一个集合,不过存储的是一个一个链表。
private static class Entry<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Entry<K,V> next;
public K getKey(){...}
public V getValue(){...}
public V setValue(V value){...}
}
Hashtable有几个关键的字段需要注意:
private int threshold; // 可容纳的极限长度,容量*负载因子
private float loadFactor; // 负载因子 该值默认为0.75
如果把Hashtbale比做桶,负载因子就表明一桶水能装半桶还是装满桶还是装四分之一桶。
负载因子越大,能装的水就越多。负载因子总和临界值配合,临界值用来表示什么时候扩容,也就是水装不下了得换一个大一点的桶装水。Hashtable每一次扩容都会扩大到原来的两倍大。
负载因子是对时间和空间的平衡,当负载因子增大空间会比较充足就不需要总是扩容,空间用的较多;如果负载因子小需要不断扩容,但是空间用的少。
通过一个put方法来了解
下图简述了put的流程

计算位置
Hashtable中计算位置特别简单,就是简单的除法
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
插入元素
首先,Hashtable需要知道当前put操作是更新旧值还是插入新值。如果更新旧值就返回旧值并更新它
下面就是一个不断查找链表的过程
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;
}
}
如果是插入新值则创建一个 Entry 并插入,这是在容量没有超过临界值的情况:
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
modCount++;
当然,如果容量超过临界值则需要扩容
扩容
if (count >= threshold) {
// 扩容,并重新计算每个元素的hash值
rehash();
// 扩容之后插入新值
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
扩容的关键是 rehash() 这个方法。该方法也很简单,只有以下几个步骤:
- 计算新的临界值
- 新临界值超过最大能接受的容量则不再扩充
- 创建一个新table(新的大桶)
- 逐个计算hash值并重新装填table

线程安全性
Hashtable是线程安全的,主要是通过为每个方法加入一个同步锁来解决,如put方法
public synchronized V put(K key, V value) {...}
但是这样性能还是比较低的,同时不能保证组合方法的线程安全性。
例如 get 和 remove
public V getAndRemove(Object o){
V v = get(o);
remove(o);
return v;
}
这样是不能保证线程安全的
分析 java.util.Hashtable 源码的更多相关文章
- java.util.Hashtable源码分析
Hashtable实现一个键值映射的表.任何非null的object可以用作key和value. 为了能存取对象,放在表里的对象必须实现hashCode和equals方法. 一个Hashtable有两 ...
- JAVA的HashTable源码分析
Hashtable简介 Hashtable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长.Hashtable ...
- 【Java】java.util.Objects 源码学习
2017-02-10 by 安静的下雪天 http://www.cnblogs.com/quiet-snowy-day/p/6387321.html 本篇概要 Objects 与 Object ...
- java.util.HashSet, java.util.LinkedHashMap, java.util.IdentityHashMap 源码阅读 (JDK 1.8)
一.java.util.HashSet 1.1 HashSet集成结构 1.2 java.util.HashSet属性 private transient HashMap<E,Object> ...
- java.util.HashSet, java.util.LinkedHashMap, java.util.IdentityHashMap 源码阅读 (JDK 1.8.0_111)
一.java.util.HashSet 1.1 HashSet集成结构 1.2 java.util.HashSet属性 private transient HashMap<E,Object> ...
- java.util.HashMap源码分析
在java jdk8中对HashMap的源码进行了优化,在jdk7中,HashMap处理“碰撞”的时候,都是采用链表来存储,当碰撞的结点很多时,查询时间是O(n). 在jdk8中,HashMap处理“ ...
- java.util.Collection源码分析和深度讲解
写在开头 java.util.Collection 作为Java开发最常用的接口之一,我们经常使用,今天我带大家一起研究一下Collection接口,希望对大家以后的编程以及系统设计能有所帮助,本文所 ...
- java.util.AbstractStringBuilder源码分析
AbstractStringBuilder是一个抽象类,是StringBuilder和StringBuffer的父类,分析它的源码对StringBuilder和StringBuffer代码的理解有很大 ...
- java.util.Dictionary源码分析
Dictionary是一个抽象类,Hashtable是它的一个子类. 类的声明:/** The <code>Dictionary</code> class is the abs ...
随机推荐
- FreeRTOS --(4)内存管理 heap3
转载自 https://blog.csdn.net/zhoutaopower/article/details/106677144 heap3 来说,是直接使用了 malloc 和 free 来直接替代 ...
- clion 预编译文件的查看
看了一圈网上也没有我能一下就能看的懂的配置教程 我就手打一篇给在用clion的同学来参考一下 本文适用于g++编译 cmake Ninja生成器 clion 默认使用的是CMAKE来构建程序 生成器用 ...
- 利用腾讯云函数部署.Net 5米游社原神每日签到功能
自从GitHub批量禁止滥用Action功能后,项目不得不考虑另外方案执行应用.其中腾讯云函数被大家作为不错的选择(虽然马上也要收费了). 但对于.Net的部署目前资源很少,而且我也没学过bash.在 ...
- 工程师姓什么很重要!别再叫我“X工”!!!
关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ 工程师之间都是这么互相打招呼的-- "高工,你设计图通过了么?" &quo ...
- 利用expect批量修改Linux服务器密码
一个执着于技术的公众号 背景 修改Linux系统密码,执行passwd即可更改密码.可如果有成千上百台服务器呢,通过ssh的方式逐一进行修改,对我们来说,工作量是非常大,且效率非常低下.因此采用批量修 ...
- Docker容器网络-基础篇
开源Linux 一个执着于技术的公众号 Docker的技术依赖于Linux内核的虚拟化技术的发展,Docker使用到的网络技术有Network Namespace.Veth设备对.Iptables/N ...
- Linux 设置开机自启动脚本(ES、MySQL、Nacos、Nginx)
/etc/rc.d/init.d 中文件会在 Linux 系统各项服务都启动完毕之后再被运行 cd /etc/rc.d/init.d:新建xxx.sh文件. chmod +x xxx.sh,赋予可执行 ...
- 128_Power BI父级排名TOPN子级动态展示
博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 前些天在和朋友交流Power BI中有这样一个需求,按照父级排名后,需要显示出父级TOPN的子级明细. 如下&l ...
- MySQL之SQL语句优化
语句优化 即优化器利用自身的优化器来对我们写的SQL进行优化,然后再将其放入InnoDB引擎中执行. 条件简化 移除不必要的括号 select * from x where ((a = 5)); 上面 ...
- React设置proxy后依旧报CROS错误
1.判断表单数据是否为后端接收的类型 POST GET2.axios自动转换问题 手动添加标头这份表单数据包括了files (二进制数据)而标头显示是JSON格式 不符 所以报CROS 更多文章请移步 ...