概述

基于J11,该类已经淘汰,如果使用线程安全的则用 ConcurrentHashMap ,用线程不安全的则使用 HashMap 。仅与HashMap进行比较

结构以及依赖关系

HashTable 的结构如下图



当遇到有同样 Hash 值的情况,会通过链表来解决冲突问题(链接法,通过链表解决冲突问题)。

链接法会随着冲突的增多导致查询时间越来越慢。会出现一种恶劣的情况,当散列算法特别差时;元素总数n和某个槽位数 m 中的 k 相等,如下图所示

在这种情况下,查找的时间为 $O(1+a)$ 其中 $O(1)$ 为hash

通过下图可以得知 Hashtable 与其他类的关系

classDiagram
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>EntryMap 的集合形式 用来遍历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() 这个方法。该方法也很简单,只有以下几个步骤:

  1. 计算新的临界值
  2. 新临界值超过最大能接受的容量则不再扩充
  3. 创建一个新table(新的大桶)
  4. 逐个计算hash值并重新装填table

线程安全性

Hashtable是线程安全的,主要是通过为每个方法加入一个同步锁来解决,如put方法

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

但是这样性能还是比较低的,同时不能保证组合方法的线程安全性。

例如 getremove

public V getAndRemove(Object o){
V v = get(o);
remove(o);
return v;
}

这样是不能保证线程安全的

分析 java.util.Hashtable 源码的更多相关文章

  1. java.util.Hashtable源码分析

    Hashtable实现一个键值映射的表.任何非null的object可以用作key和value. 为了能存取对象,放在表里的对象必须实现hashCode和equals方法. 一个Hashtable有两 ...

  2. JAVA的HashTable源码分析

    Hashtable简介 Hashtable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长.Hashtable ...

  3. 【Java】java.util.Objects 源码学习

    2017-02-10 by 安静的下雪天  http://www.cnblogs.com/quiet-snowy-day/p/6387321.html    本篇概要 Objects 与 Object ...

  4. 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> ...

  5. 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> ...

  6. java.util.HashMap源码分析

    在java jdk8中对HashMap的源码进行了优化,在jdk7中,HashMap处理“碰撞”的时候,都是采用链表来存储,当碰撞的结点很多时,查询时间是O(n). 在jdk8中,HashMap处理“ ...

  7. java.util.Collection源码分析和深度讲解

    写在开头 java.util.Collection 作为Java开发最常用的接口之一,我们经常使用,今天我带大家一起研究一下Collection接口,希望对大家以后的编程以及系统设计能有所帮助,本文所 ...

  8. java.util.AbstractStringBuilder源码分析

    AbstractStringBuilder是一个抽象类,是StringBuilder和StringBuffer的父类,分析它的源码对StringBuilder和StringBuffer代码的理解有很大 ...

  9. java.util.Dictionary源码分析

    Dictionary是一个抽象类,Hashtable是它的一个子类. 类的声明:/** The <code>Dictionary</code> class is the abs ...

随机推荐

  1. 一文看懂二层接口、三层接口、PVID及VLANIF

    关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ 文章来源:朱仕耿个人博客 一位同事问了个关于二层和三层端口的概念及区分,以及关于VLANIF. ...

  2. 太极限了,JDK的这个BUG都能被我踩到

    hello,大家好呀,我是小楼. 之前遇到个文件监听变更的问题,刚好这周末有空研究了一番,整理出来分享给大家. 从一次故障说起 我们还是从故障说起,这样更加贴近实际,也能让大家更快速理解背景. 有一个 ...

  3. 强制20天加班开发app后被集体解雇,象寻技术负责人公众号发文怒斥前领导

    5月16日下午三点,象寻官方公众号发了一篇<祝象寻早日倒闭的文章>文章,文章配一个竖中指的手势.如此劲爆的文章瞬间引爆了微信朋友圈,大家纷纷分享给好友和微信群,阅读量也达到了十万+. 当时 ...

  4. linux篇-linux面试题汇总

    Linux经典面试题,看看你会几题? 1. 在Linux系统中,以 文件 方式访问设备 . 2. Linux内核引导时,从文件 /etc/fstab 中读取要加载的文件系统. 3. Linux文件系统 ...

  5. WPF 分组

    分组和树形结构是不一样的. 树形结构是以递归形式存在.分组是以键值对存在的形式,类似于GroupBy这样的形式. 举个例子 ID NAME SEX Class 1 张三 男 1 2 李四 女 2 3 ...

  6. 使用 gitbook 制作自己的 html 文档

    使用 gitbook 制作自己的 html 文档 步骤如下 npm install gitbook-cli -g // 全局安装 gitbook-cli <span style="te ...

  7. python面向对象(封装、多态、反射)

    目录 面向对象之封装 @property 面向对象之多态 面向对象之反射 面向对象之封装 含义 将类中的某些名字按照特殊的书写方式"隐藏"起来,不让外界直接调用,目的是为了不然外界 ...

  8. SQL Server 2017 各版本之间的差异

    SQL Server 2017的亮点 您选择的语言和平台 使用您选择的语言在本地和云中(现在在 Windows.Linux 和 Docker 容器上)构建现代应用程序. 行业领先的性能 充分利用任务关 ...

  9. npm错误:Cannot find module ‘compression-webpack-plugin

    转自 (82条消息) 前端开发遇到Cannot find module 'compression-webpack-plugin'问题解决_brave_zhao的博客-CSDN博客 <div id ...

  10. 解决跨海高并发崩溃难题?so easy

    近年来随着互联网强势的发展浪潮,越来越多的企业选择跨境出海,扩展海外市场.而想要在一个陌生市场最快速地吸引到用户,一定不能缺少的就是丰富多样的各类活动.然而活动在带来大流量的同时,也带来了一些问题,比 ...