HashTable源码
一直写一些工作日常笔记,最近想静下来看下一些常见的知识原理,在很多blog上都有看到过,不过看到的东西是别人的,只有自己去看了源码,写了demo,理解了才是自己的东西。
hashTable(jdk1.7)
定义:
继承与Dictionary,实现了Map的一些方法,标记了这个对象Clone,以及序列化Serializable
public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, java.io.Serializable
构造器及初始化:
不传任何参数的构造器,系统默认容量为11,hash因子0.75
new Hashtable();//this(11, 0.75f);
传入容量大小的initialCapacity,
Hashtable(int initialCapacity) {this(initialCapacity, 0.75f);}
传入容量(initialCapacity)和hash因子(loadFactor)
new Hashtable(int initialCapacity, float loadFactor)
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,设置一个容量为,保证传入不是IsEmptyif (initialCapacity==0)initialCapacity = 1;this.loadFactor = loadFactor;//创建一个Entry数组,Entry是一个单链表结构table = new Entry[initialCapacity];//设置一个阀值threshold=容量*hash因子,超过这个阀值,需要对数组扩容threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);initHashSeedAsNeeded(initialCapacity);
}
构造其中主要是初始化容量值,hash因子,threshold,创建一个单链表对象数组。还看到一个initHashSeedAsNeeded方法,这个方法主要初始化hashSeed参数,其中hashSeed用于计算key的hash值,它与key的hashCode进行按位异或运算。这个hashSeed是一个与实例相关的随机值,主要用于解决hash冲突。
重要方法:
public synchronized V put(K key, V value) {// Make sure the value is not nullif (value == null) {throw new NullPointerException();}// Makes sure the key is not already in the hashtable.Entry tab[] = table;//使用hashSend与运算key.hashCodeint hash = hash(key);//hash & 0x7FFFFFFF防止hash为负值吧 //这里使用除法散列int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {//遇到hash值相同的插入,使用单链表连接起来,解决hash冲突if ((e.hash == hash) && e.key.equals(key)) {V old = e.value;e.value = value;return old;}}//modCount没修改一次计数加1,与添加数量无关,用于遍历时候检查操作合法性modCount++;if (count >= threshold) {// Rehash the table if the threshold is exceeded//重hash 数组中元素超过阀值,需要再次计算容量,扩容数组rehash();tab = table;hash = hash(key);index = (hash & 0x7FFFFFFF) % tab.length;}// Creates the new entry.每次扩容创建新数组,开销比较大,所以给定好初始容量比较重要,减少数组扩容Entry<K,V> e = tab[index];tab[index] = new Entry<>(hash, key, value, e);count++;return null;}
put方法比较简单,就是先对key值hash然后与hashSend这个随机值与运行,这样做的好处是减少hash重复。然后使用除法散列法,把对象放入到entry数组,如果遇到hash值重复,使用单链表把相同hash值的元素连接起来。但数组达到阀值(threshold),对创建一个新数组进行扩容。
接下来看下rehash
protected void rehash() {int oldCapacity = table.length;Entry<K,V>[] oldMap = table;// overflow-conscious code//计算新数组容量,左移1位,new=2*old+1int newCapacity = (oldCapacity << 1) + 1;if (newCapacity - MAX_ARRAY_SIZE > 0) {if (oldCapacity == MAX_ARRAY_SIZE)// Keep running with MAX_ARRAY_SIZE bucketsreturn;newCapacity = MAX_ARRAY_SIZE;}Entry<K,V>[] newMap = new Entry[newCapacity];modCount++;//重新生成阀值threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);boolean rehash = initHashSeedAsNeeded(newCapacity);table = newMap;//将原来的元素拷贝到新的HashTable中 for (int i = oldCapacity ; i-- > 0 ;) {for (Entry<K,V> old = oldMap[i] ; old != null ; ) {Entry<K,V> e = old;old = old.next;if (rehash) {e.hash = hash(e.key);}int index = (e.hash & 0x7FFFFFFF) % newCapacity;e.next = newMap[index];newMap[index] = e;}}}
接下看看putAll,这个方法没什么说的,遍历传入集合,最终还是调用put方法
public synchronized void putAll(Map<? extends K, ? extends V> t) {for (Map.Entry<? extends K, ? extends V> e : t.entrySet())put(e.getKey(), e.getValue());}
get方法 先hash转入key,然后找到对应的index,在entry数组中找到对应的entry对象,再通过hash值和key比较找到想要的对象值返回
public synchronized V get(Object key) {Entry tab[] = table;int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {//hash重复,判断key是否同一个对象if ((e.hash == hash) && e.key.equals(key)) {return e.value;}}return null;}
看完获取,添加元素后,再来看看删除
同样先hash转入key,然后找到对应的index,在entry数组中找到对应的entry对象,再通过hash值和key比较找到想要的对象,如果当前entry没有prev,直接把该对象的next指向tab[index],断开该对象的链表;
如果该对象有prev,和next就把该对象的prev.next指向该对象的next
public synchronized V remove(Object key) {Entry tab[] = table;int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {modCount++;if (prev != null) {prev.next = e.next;} else {tab[index] = e.next;}count--;V oldValue = e.value;e.value = null;return oldValue;}}return null;}
clear没什么可说的 把所有的元素置null
public synchronized void clear() {Entry tab[] = table;modCount++;for (int index = tab.length; --index >= 0; )tab[index] = null;count = 0;}
迭代器Enumeration:
hashTable使用的是Enumeration迭代器接口
Enumeration接口是JDK1.0时推出的,最早使用Vector时就是使用Enumeration接口进行输出。虽然Enumeration是一个旧的类,但是在JDK1.5之后为Enumeration类进行了扩充,增加了泛型的操作应用。
Enumeration接口常用的方法有hasMoreElements()(判断是否有下一个值)和 nextElement()(取出当前元素),这些方法的功能跟Iterator类似,只是Iterator中存在删除数据的方法,而此接口不存在删除操作。
HashTable与HashMap的区别
第一:我们从他们的定义就可以看出他们的不同,HashTable基于Dictionary类,而HashMap是基于AbstractMap。
(Dictionary是什么?它是任何可将键映射到相应值的类的抽象父类,而AbstractMap是基于Map接口的骨干实现,它以最大限度地减少实现此接口所需的工作。)
第二:HashMap可以允许存在一个为null的key和任意个为null的value,但是HashTable中的key和value都不允许为null。
第三:迭代器不同,hashTable使用的比较古老的Enumeration,而hashMap使用的是Interator
第四:Hashtable的方法是同步的,而HashMap的方法不是。所以有人一般都建议如果是涉及到多线程同步时采用HashTable,没有涉及就采用HashMap,
HashTable源码相关推荐
- Java源码详解三:Hashtable源码分析--openjdk java 11源码
文章目录 注释 哈希算法与映射 线程安全的实现方法 put 操作 get操作 本系列是Java详解,专栏地址:Java源码分析 Hashtable官方文档:Hashtable (Java Platfo ...
- hashtable源码解析
Hashtable 也就是哈希表,是个非常重要的概率,在剖析hashtable源码前,我先简单介绍一下hashtable的原理 哈希表概念 什么是哈希(hash又称散列)? 将任意长度的消息压缩到某一 ...
- Java8 Hashtable 源码阅读
一.Hashtable 概述 Hashtable 底层基于数组与链表实现,通过 synchronized 关键字保证在多线程的环境下仍然可以正常使用.虽然在多线程环境下有了更好的替代者 Concurr ...
- 【Java集合源码剖析】Hashtable源码剖析
转载请注明出处:http://blog.csdn.net/ns_code/article/details/36191279 Hashtable简介 Hashtable同样是基于哈希表实现的,同样每个元 ...
- Java集合:Hashtable源码分析
1. 概述 上次讨论了HashMap的结构,原理和实现,本文来对Map家族的另外一个常用集合HashTable进行介绍.HashTable和HashMap两种集合非常相似,经常被各种面试官问到两者的区 ...
- 带你深入浅出的分析 HashTable 源码
Hashtable 一个元老级的集合类,早在 JDK 1.0 就诞生了,今天小编想和大家一起来揭开它的面纱! 01.摘要 在集合系列的第一章,咱们了解到,Map 的实现类有 HashMap.Linke ...
- 万字长文|Hashtable源码深度解析以及与HashMap的区别
基于JDK1.8对Java中的Hashtable集合的源码进行了深度解析,包括各种方法.扩容机制.哈希算法.遍历方法等方法的底层实现,最后给出了Hashtable和HashMap的详细对比以及使用建议 ...
- HashTable 源码解读
很多人都知道HashTable与HashMap的关系,HashTable是线程安全的,HashMap是非线程安全的.在介绍完HashMap之后,趁热介绍一下HashTable.在HashTable中没 ...
- 【Java源码分析】HashTable源码分析
类的定义 public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Clon ...
- Hashtable源码注释
2019独角兽企业重金招聘Python工程师标准>>> package java.util; import java.io.*;public class Hashtable<K ...
最新文章
- 墨迹天气接口html,moji_weather_spider.py
- ocv特性_实际使用工况的锂离子电池SOC-OCV关系
- ImportError: cannot import name 'six'解决
- 论文浅尝 | 面向单关系事实问题的中文问答模型
- html5填空题阅卷,“过五关”般严格,高考阅卷老师来自哪里?研究生参与阅卷?
- 爬虫数据传入mysql_nodejs爬虫数据存入mysql
- 计算机毕业设计题目汇总 - 简单的毕设选题
- mrp手机qq2008下载-MRP格式的手机QQ2008软件介绍及如何正确安装MRPQQ2008挂Q版
- 用计算机名共享打印机不能打印,共享打印机无法打印怎么办解决教程
- 【精华】PS十大抠图技法(上)
- redis 客户端连接及常用命令使用
- 回给collapsar的信
- 农林牧渔行业S2B2C系统网站提升品牌知名度,提升盈利水平
- 经典排序算法学习笔记二——快速排序
- 机器学习中类别变量的编码方法总结
- 单片机炫彩灯实训报告_51单片机呼吸灯实验报告.doc
- 程序自动修复相关工作总结
- 软件测试外包的战术有哪些
- GroupM小结1(14年8月28日)
- 信号与系统(13)- 傅里叶变换的性质