HashTable源码学习
一.介绍
1.HashMap和HashTable的区别
1.相同点
- 二者都实现了Map接口。
- 底层都是哈西表
2.不同点
Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。
Hashtable 第一次创建对象的时候就会给底层数组开辟空间 Entry[] 11
HashMap 创建对象时 没有给底层数组进行空间开辟
HashMap把Hashtable的contains方法去掉了。改成containsValue和containsKey
Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。
Hashtable中,key和value都不允许出现null值
HashMap中,null可以作为键,这样的键只有一个
哈希值的使用不同,HashTable直接使用对象的hashCode
HashMap重新计算hash值。
HashTable在不指定容量的情况下的默认容量为11
HashMap为16
Hashtable不要求底层数组的容量一定要为2的整数次幂
HashMap则要求一定为2的整数次幂。
Hashtable扩容时,将容量变为原来的2倍加1
HashMap扩容时,将容量变为原来的2倍。
Hashtable 中的方法是Synchronize的。
HashMap线程不安全
计算index的方法不同:
- HashTable: index = (hash & 0x7FFFFFFF) % tab.length
- HashMap:index = hash & (tab.length – 1)
2.HashTable
像HashMap一样,Hashtable在哈希表中存储键/值对。当使用一个哈希表,要指定用作键的对象,以及要链接到该键的值。
然后,该键经过哈希处理,所得到的散列码被用作存储在该表中值的索引
二.源码部分
1.基本属性
继承Dictionary 类
是一个抽象类,用来存储键/值对,作用和Map类相似。
给出键和值,你就可以将值存储在Dictionary对象中。一旦该值被存储,就可以通过它的键来获取它。所以和Map一样, Dictionary 也可以作为一个键/值对列表。
Cloneable是标记型的接口,它们内部都没有方法和属性,实现 Cloneable来表示该对象能被克隆
java.io.Serializable接口:
可以启用其序列化功能。未实现次接口的类无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
/**
* The hash table data.哈希表数据
*/
private transient Entry<?,?>[] table;
/**
* The total number of entries in the hash table.哈希表中enyty的总数
*/
private transient int count;
/**
* The table is rehashed when its size exceeds this threshold. (The
* value of this field is (int)(capacity * loadFactor).)
* 阈值,边界值
* @serial
*/
private int threshold;
/**
* The load factor for the hashtable.
* 加载因子、负载因子
* @serial
*/
private float loadFactor;
/**
* The number of times this Hashtable has been structurally modified
* Structural modifications are those that change the number of entries in
* the Hashtable or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the Hashtable fail-fast. (See ConcurrentModificationException).
*/
private transient int modCount = 0;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = 1421746759512286392L;
/**
* The maximum size of array to allocate.
* 要分配的数组的最大大小。
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
2.构造函数
/**
* 无参构造,默认容量为11,加载因子为0.75
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
public Hashtable() {
this(11, 0.75f);
}
/**
* Constructs a new, empty hashtable with the specified initial capacity
* and default load factor (0.75).
* 创建指定大小的哈希表
* 初始容量用户自定义,加载因子为默认的0.75
* @param initialCapacity the initial capacity of the hashtable.
* @exception IllegalArgumentException if the initial capacity is less
* than zero.
*/
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
/**
* Constructs a new, empty hashtable with the specified initial
* capacity and the specified load factor.
* 创建了一个指定大小的哈希表,并且通过fillRatio指定填充比例
* @param initialCapacity the initial capacity of the hashtable.
* @param loadFactor the load factor of the hashtable.
* @exception IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive.
*/
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);
//当初始化容量为0的时候,将初始容量设置为1
if (initialCapacity==0)
initialCapacity = 1;
//加载因子赋值
this.loadFactor = loadFactor;
//创建一个新的大小为初始容量的enytry数组
table = new Entry<?,?>[initialCapacity];
//阈值为initialCapacity * loadFactor或最大值
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
/**
* Constructs a new hashtable with the same mappings as the given
* Map. The hashtable is created with an initial capacity sufficient to
* hold the mappings in the given Map and a default load factor (0.75).
* 第四个构造方法创建了一个以M中元素为初始化元素的哈希表。
* 哈希表的容量被设置为M的两倍,或是11
* @param t the map whose mappings are to be placed in this map.
* @throws NullPointerException if the specified map is null.
* @since 1.2
*/
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
3.contains()
/**
* Tests if some key maps into the specified value in this hashtable.
* This operation is more expensive than the {@link #containsKey
* containsKey} method.
* 测试此映射表中是否存在与指定值关联的键。
* <p>Note that this method is identical in functionality to
* {@link #containsValue containsValue}, (which is part of the
* {@link Map} interface in the collections framework).
*
* @param value a value to search for
* @return <code>true</code> if and only if some key maps to the
* <code>value</code> argument in this hashtable as
* determined by the <tt>equals</tt> method;
* <code>false</code> otherwise.
* @exception NullPointerException if the value is <code>null</code>
*/
public synchronized boolean contains(Object value) {
//如果传入的value值为空,则抛出异常
if (value == null) {
throw new NullPointerException();
}
//2层循环遍历数组及其链表
Entry<?,?> tab[] = table;
for (int i = tab.length ; i-- > 0 ;) {
for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
if (e.value.equals(value)) {
return true;
}
}
}
return false;
}
4.put()
/**
* Maps the specified <code>key</code> to the specified
* <code>value</code> in this hashtable. Neither the key nor the
* value can be <code>null</code>. <p>
*
* The value can be retrieved by calling the <code>get</code> method
* with a key that is equal to the original key.
* 将指定 key 映射到此哈希表中的指定 value。
* @param key the hashtable key
* @param value the value
* @return the previous value of the specified key in this hashtable,
* or <code>null</code> if it did not have one
* @exception NullPointerException if the key or value is
* <code>null</code>
* @see Object#equals(Object)
* @see #get(Object)
*/
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.
Entry<?,?> tab[] = table;
//得到哈希值
int hash = key.hashCode();
//得到数组下标
int index = (hash & 0x7FFFFFFF) % tab.length;
//忽略警告
@SuppressWarnings("unchecked")
//将对应的数组元素保留起来
Entry<K,V> entry = (Entry<K,V>)tab[index];
//当该位置已有元素,则发生哈希冲突,遍历链表找通过equal方法找到key
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
//覆盖原来的值
V old = entry.value;
entry.value = value;
//返回原来的值
return old;
}
}
5.addEntry()
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
//如果数组有效值等于阈值,将进行扩容操作,然后在计算新的index
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.创建新条目。count++
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
6.扩容机制
/**
* Increases the capacity of and internally reorganizes this
* hashtable,增加hashtable的容量并对其重组
* in order to accommodate and access its entries more
* efficiently.
* 以便高效地访问更多的条目
* This method is called automatically when the number of keys in the hashtable exceeds this hashtable's capacity and load factor.
* 当哈希表中的键数超过该哈希表的容量和负载因子时,将自动调用此方法。
*/
@SuppressWarnings("unchecked")
protected void rehash() {
//先将数组原来的容量保存起来
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
//新的容量为原来的一半 + 1
int newCapacity = (oldCapacity << 1) + 1;
//如果新的容量超出了最大值,则将新容量变为最大值
if (newCapacity - MAX_ARRAY_SIZE > 0) {
//如果原来的容量就是最大值则将不扩容,return
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
//新建一个新容量大小的数组
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
//阈值也重新赋值,如若已经超出最大值,则为MAX_ARRAY_SIZE + 1
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
//将table指向新数组
table = newMap;
//遍历原来的hashtable,将其拷贝过来
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;
//数组下标值不同于hashmap,都重新计算
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
三.总结
- Hash:是一种信息摘要算法,它还叫做哈希,或者散列。我们平时使用的MD5,SHA1都属于Hash算法,通过输入key进行Hash计算,就可以获取key的HashCode(),比如我们通过校验MD5来验证文件的完整性。
- Hashtable 实现并发安全的原理是通过
synchronized 关键字
- 当线程数量增加的时候,Hashtable 的性能会急剧下降,因为每一次修改都需要锁住整个对象,而其他线程在此期间是不能操作的。不仅如此,还会带来额外的上下文切换等开销,所以此时它的吞吐量甚至还不如单线程的情况。
HashTable源码学习的更多相关文章
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...
- HashMap与HashTable源码学习及效率比较分析
一.个人学习后的见解: 首先表明学习源码后的个人见解,后续一次依次进行分析: 1.线程安全:HashMap是非线程安全的,HashTable是线程安全的(HashTable中使用了synchroniz ...
- Dapper源码学习和源码修改
之前ORM比较火热,自己也搞了个WangSql,但是感觉比较low,大家都说Dapper性能好,所以现在学习学习Dapper,下面简单从宏观层面讲讲我学习的Dapper. 再了解一个东西前,先得学会使 ...
- Hadoop源码学习笔记(1) ——第二季开始——找到Main函数及读一读Configure类
Hadoop源码学习笔记(1) ——找到Main函数及读一读Configure类 前面在第一季中,我们简单地研究了下Hadoop是什么,怎么用.在这开源的大牛作品的诱惑下,接下来我们要研究一下它是如何 ...
- 并发-HashMap和HashTable源码分析
HashMap和HashTable源码分析 参考: https://blog.csdn.net/luanlouis/article/details/41576373 http://www.cnblog ...
- HashMap的源码学习以及性能分析
HashMap的源码学习以及性能分析 一).Map接口的实现类 HashTable.HashMap.LinkedHashMap.TreeMap 二).HashMap和HashTable的区别 1).H ...
- Java入门系列之集合Hashtable源码分析(十一)
前言 上一节我们实现了散列算法并对冲突解决我们使用了开放地址法和链地址法两种方式,本节我们来详细分析源码,看看源码中对于冲突是使用的哪一种方式以及对比我们所实现的,有哪些可以进行改造的地方. Hash ...
- Java并发包源码学习系列:JDK1.8的ConcurrentHashMap源码解析
目录 为什么要使用ConcurrentHashMap? ConcurrentHashMap的结构特点 Java8之前 Java8之后 基本常量 重要成员变量 构造方法 tableSizeFor put ...
- JUC源码学习笔记4——原子类,CAS,Volatile内存屏障,缓存伪共享与UnSafe相关方法
JUC源码学习笔记4--原子类,CAS,Volatile内存屏障,缓存伪共享与UnSafe相关方法 volatile的原理和内存屏障参考<Java并发编程的艺术> 原子类源码基于JDK8 ...
随机推荐
- 快速搭建Hadoop-Hive-Zoopkeeper-Sqoop环境进入Sqoop学习环境
原文链接: https://www.toutiao.com/i6771763211927552523/ CDH简单了解 CDH: C:cloudera(公司) D:distribute H:Hadoo ...
- css3中transition属性详解
css3中通过transition属性可以实现一些简单的动画过渡效果~ 1.语法 transition: property duration timing-function delay; transi ...
- vue2如何根据不同的环境配置不同的baseUrl
在正常的开发中,通常我们需要在线上的测试环境中运行代码来检查是否有些线上才会出现的bug或者是问题.每次去特意的修改我们的baseUrl显然是不现实的,而且说不定哪天忘记了估计会被大佬喷死 首先,这是 ...
- 【linux】Ubuntu20.04使用apt下载和卸载openJDK
Ubuntu20.04使用apt下载和卸载openJDK 前言 由于最近电脑装了ubuntu和win双系统,想再ubuntu上学习.在成功配置完系统之后,开始了配学习环境的旅程.... 这次的是使用u ...
- insert语句
7.4.插入数据insert(DML语句) 语法格式: insert into 表名(字段名1,字段名2,字段名3...) values(值1,值2,值3): 注意:字段名和值要一一对应.什么是一一对 ...
- IoC容器-Bean管理XML方式(p名称空间注入)
5,p名称空间注入(简化xml配置) (1)使用p名称空间注入,可以简化基于xml配置方式 (了解实际用不多) 第一步 添加 p 名称空间在配置文件中 第二步 进行属性注入,在bean标签里面进行 ...
- dubbo-gateway 高性能dubbo网关
dubbo-gateway dubbo-gateway 提供了http协议到dubbo协议的转换,但[并非]使用dubbo的[泛化]调用(泛化调用性能比普通调用有10-20%的损耗,通过普通异步的调用 ...
- python组合
目录 一:组合基础使用 二:组合 一:组合基础使用 组合: 就是一个对象拥有一个属性,该属性的值是另一个对象 继承:满足什么是什么的关系,is-a的关系 继承是一把双刃剑,单继承能实现就尽量少的继承, ...
- 一起玩转玩转LiteOS组件:TinyFrame
摘要:TinyFrame是一个简单的用于解析串口(如 UART.telnet.套接字等)通信数据帧的库. 本文分享自华为云社区<LiteOS组件尝鲜-玩转TinyFrame>,作者:Lio ...
- python网络爬虫-动态网页抓取(五)
动态抓取的实例 在开始爬虫之前,我们需要了解一下Ajax(异步请求).它的价值在于在与后台进行少量的数据交换就可以使网页实现异步更新. 如果使用Ajax加载的动态网页抓取,有两种方法: 通过浏览器审查 ...