JDK源码阅读(5):HashTable类阅读笔记
HashTable
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
...
}
HashMap只实现了Map接口,而HashTable还继承了Dictionary类。但实际上Dictionary类只是一个历史遗留问题,任何新的键值对集合都只需要实现Map接口。
1. 构造方法
/**
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
public Hashtable() {
this(11, 0.75f);
}
HashTable的默认容量是11,默认负载因子是0.75。HashMap的这两个值分别是16和0.75。
2. Properties类和Void类
在阅读到HashTable的构造函数时,我看到了一个奇怪的构造函数:
/**
* A constructor chained from {@link Properties} keeps Hashtable fields
* uninitialized since they are not used.
*
* @param dummy a dummy parameter
*/
Hashtable(Void dummy) {}
首先,在参数列表中出现了一个我从来没见过的类:Void类。我们进入查看这个类的定义。
package java.lang;
/**
* The {@code Void} class is an uninstantiable placeholder class to hold a
* reference to the {@code Class} object representing the Java keyword
* void.
*
* @author unascribed
* @since 1.1
*/
public final class Void {
/**
* The {@code Class} object representing the pseudo-type corresponding to
* the keyword {@code void}.
*/
@SuppressWarnings("unchecked")
public static final Class<Void> TYPE = (Class<Void>) Class.getPrimitiveClass("void");
/*
* The Void class cannot be instantiated.
*/
private Void() {}
}
可以看到,Void类是一个final类,同时只有一个私有的构造函数。显然,这个类既不可以被继承,也不可以被实例化。在doc中我们得知,这是一个占位符类,用于保存对表示Java关键字void的Class对象的引用。
我们可以想到,假如将Void类作为一个方法的返回类型,这意味着这个方法只能返回null。
public Void f(){
...
}
那么在这里作为参数的Void类又起到怎样的功能呢?继续阅读构造方法上方的doc,我们发现,这个函数是供java.util.Properties类使用的,Properties类中有下面这个构造函数。
private Properties(Properties defaults, int initialCapacity) {
// use package-private constructor to
// initialize unused fields with dummy values
super((Void) null);
map = new ConcurrentHashMap<>(initialCapacity);
this.defaults = defaults;
// Ensure writes can't be reordered
UNSAFE.storeFence();
}
Properties类是HashTable类的子类,在这个构造函数中它调用了HashTable中包级私有的构造方法。如果没有这个super语句的话,就会默认调用父类HashTable的无参构造函数,但显然,这是没有必要的。通过在HashTable中添加一个伪构造方法供Properties类调用,可以避免这种无必要的开销。
这里顺便说明一下Properties类。
Properties类表示一组持久的属性。表示一个持久的属性集,属性列表中每个键及其对应值都是一个字符串。
Properties 类被许多 Java 类使用。例如,在获取环境变量时它就作为 System.getProperties() 方法的返回值。
Properties 定义如下实例变量.这个变量持有一个 Properties 对象相关的默认属性列表。
Properties defaults;
3. HashTable中实现线程安全的方式
HashTable中为几乎所有业务方法都添加了synchronized关键字,实现了线程安全。
public synchronized int size() {...}
public synchronized boolean isEmpty() {...}
public synchronized V put(K key, V value) {...}
public synchronized V get(Object key) {...}
public synchronized V remove(Object key) {...}
4. keys方法和elements方法
public synchronized Enumeration<K> keys() {
return this.<K>getEnumeration(KEYS);
}
public synchronized Enumeration<V> elements() {
return this.<V>getEnumeration(VALUES);
}
返回了键集合和值集合的枚举。
private <T> Enumeration<T> getEnumeration(int type) {
if (count == 0) {
return Collections.emptyEnumeration();
} else {
return new Enumerator<>(type, false);
}
}
下面是作为HashTable类内部类的枚举类
private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
final Entry<?,?>[] table = Hashtable.this.table;
int index = table.length;
Entry<?,?> entry;
Entry<?,?> lastReturned;
final int type;
/**
* Indicates whether this Enumerator is serving as an Iterator
* or an Enumeration. (true -> Iterator).
*/
final boolean iterator;
/**
* The modCount value that the iterator believes that the backing
* Hashtable should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
protected int expectedModCount = Hashtable.this.modCount;
Enumerator(int type, boolean iterator) {
this.type = type;
this.iterator = iterator;
}
public boolean hasMoreElements() {
...
}
@SuppressWarnings("unchecked")
public T nextElement() {
Entry<?,?> et = entry;
int i = index;
Entry<?,?>[] t = table;
/* Use locals for faster loop iteration */
while (et == null && i > 0) {
et = t[--i];
}
entry = et;
index = i;
if (et != null) {
Entry<?,?> e = lastReturned = entry;
entry = e.next;
return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
}
throw new NoSuchElementException("Hashtable Enumerator");
}
// Iterator methods
public boolean hasNext() {
return hasMoreElements();
}
public T next() {
if (Hashtable.this.modCount != expectedModCount)
throw new ConcurrentModificationException();
return nextElement();
}
public void remove() {
...
}
}
5. HashTable中定位下标的方法
int index = (hash & 0x7FFFFFFF) % tab.length;
hash & 0x7FFFFFFF是为了保证hash值是一个正数,即二进制下第一位是0。
然后直接对length取模。
6. rehash方法和addEntry方法
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// 扩容逻辑:乘二加一
int newCapacity = (oldCapacity << 1) + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// 原容量就满了,则直接返回
return;
newCapacity = MAX_ARRAY_SIZE;
}
// 创建新表
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;
}
}
}
该方法会增加哈希表的容量并在内部重新组织哈希表。当哈希表中的键数超过此哈希表的容量*负载因子时,将自动调用此方法。扩容的逻辑是容量*2+1。
下面的方法用于向表中假如键值对,在put等方法中都有调用。
private void addEntry(int hash, K key, V value, int index) {
Entry<?,?> tab[] = table;
if (count >= threshold) {
// 先调用rehash()
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++;
modCount++;
}
JDK源码阅读(5):HashTable类阅读笔记的更多相关文章
- 【java源码】解读HashTable类背后的实现细节
HashTable这个类实现了哈希表从key映射到value的数据结构形式.任何非null的对象都可以作为key或者value. 要在hashtable中存储和检索对象,作为key的对象必须实现has ...
- 【并发编程】【JDK源码】JDK的(J.U.C)java.util.concurrent包结构
本文从JDK源码包中截取出concurrent包的所有类,对该包整体结构进行一个概述. 在JDK1.5之前,Java中要进行并发编程时,通常需要由程序员独立完成代码实现.当然也有一些开源的框架提供了这 ...
- JDK源码那些事儿之浅析Thread上篇
JAVA中多线程的操作对于初学者而言是比较难理解的,其实联想到底层操作系统时我们可能会稍微明白些,对于程序而言最终都是硬件上运行二进制指令,然而,这些又太过底层,今天来看一下JAVA中的线程,浅析JD ...
- JDK源码阅读(一):Object源码分析
最近经过某大佬的建议准备阅读一下JDK的源码来提升一下自己 所以开始写JDK源码分析的文章 阅读JDK版本为1.8 目录 Object结构图 构造器 equals 方法 getClass 方法 has ...
- jdk源码阅读笔记-HashSet
通过阅读源码发现,HashSet底层的实现源码其实就是调用HashMap的方法实现的,所以如果你阅读过HashMap或对HashMap比较熟悉的话,那么阅读HashSet就很轻松,也很容易理解了.我之 ...
- 如何阅读jdk源码?
简介 这篇文章主要讲述jdk本身的源码该如何阅读,关于各种框架的源码阅读我们后面再一起探讨. 笔者认为阅读源码主要包括下面几个步骤. 设定目标 凡事皆有目的,阅读源码也是一样. 从大的方面来说,我们阅 ...
- JDK源码阅读顺序
很多java开发的小伙伴都会阅读jdk源码,然而确不知道应该从哪读起.以下为小编整理的通常所需阅读的源码范围. 标题为包名,后面序号为优先级1-4,优先级递减 1.java.lang 1) Obj ...
- JDK源码阅读(三):ArraryList源码解析
今天来看一下ArrayList的源码 目录 介绍 继承结构 属性 构造方法 add方法 remove方法 修改方法 获取元素 size()方法 isEmpty方法 clear方法 循环数组 1.介绍 ...
- 如何阅读JDK源码
JDK源码阅读笔记: https://github.com/kangjianwei/LearningJDK 如何阅读源码,是每个程序员需要面临的一项挑战. 为什么需要阅读源码?从实用性的角度来看,主要 ...
- 关于JDK源码:我想聊聊如何更高效地阅读
简介 大家好,我是彤哥,今天我想和大家再聊聊JDK源码的几个问题: 为什么要看JDK源码 JDK源码的阅读顺序 JDK源码的阅读方法 为什么要看JDK源码 一,JDK源码是其它所有源码的基础,看懂了J ...
随机推荐
- navicat导出DDL语句
工作中有的时候需要将某个库中的表.视图.函数.存储过程等创建语句导出,又不需要表中的数据. 方法一:需要拷贝的创建语句条数不多,可以选择直接拷贝DDL语句 方法二:使用Navicat的备份功能
- python对象引用和垃圾回收
变量="标签" 变量a和变量b引用同一个列表: >>> a = [1, 2, 3] >>> b = a >>> a.appen ...
- 『Python』matplotlib常用函数
1. 绘制图表组成元素的主要函数 1.1 plot()--展现量的变化趋势 import numpy as np import matplotlib.pyplot as plt import matp ...
- [转载]php连接postgreSQL数据库及其操作(php5,postgreSQL9)
数据库连接:dbconn.php<?php$conn = pg_connect("host=localhost port=5432 dbname=myd user=postgres p ...
- CF1039D-You Are Given a Tree【根号分治,贪心】
正题 题目链接:https://www.luogu.com.cn/problem/CF1039D 题目大意 给出\(n\)个点的一棵树,然后对于\(k\in[1,n]\)求每次使用一条长度为\(k\) ...
- P6177-Count on a tree II/[模板]树分块
正题 题目链接:https://www.luogu.com.cn/problem/P6177 题目大意 \(n\)个点的一棵树\(m\)次询问树上颜色. 强制在线 \(1\leq n\leq 4\ti ...
- P7294-[USACO21JAN]Minimum Cost Paths P【单调栈】
正题 题目链接:https://www.luogu.com.cn/problem/P7294 题目大意 \(n\times m\)的网格,当你在\((x,y)\)时你有两种选择 花费\(x^2\)的代 ...
- Pycharm软件学生和老师可申请免费专业版激活码
有一种邮箱,叫做教育邮箱,这东西在这个互联网的世界有很大的优惠及特权,在 Jetbrain 这里, 如果你有教育邮箱(没有教育邮箱怎么办?.edu.cn后缀的邮箱)但很多学生.甚至老师都未必有. 你只 ...
- Vue组件间的数据传输
1.父组件向子组件传输数据:自定义属性 1 //父组件 2 <Son :msg="message" :user="userinfo"></So ...
- 前端开发3年了,竟然不知道什么是 Vue 脚手架?(上)
一.脚手架认识和使用前提 CLI 是什么意思? CLI -- Command-Line Interface 命令行界面,俗称脚手架. 脚手架就是一个大概的框架,是建筑学上的一个概念. 1.1.什么是V ...