一、结构图

Hashtable采用桶位+链表结构实现,如下图所示:

二、属性

2.1 table

底层是一个存储Entry的数组

private transient Entry<?,?>[] table;

2.2 Entry

一个单向链表结构

    private static class Entry<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Entry<K,V> next;protected Entry(int hash, K key, V value, Entry<K,V> next) {this.hash = hash;this.key =  key;this.value = value;this.next = next;}@SuppressWarnings("unchecked")protected Object clone() {return new Entry<>(hash, key, value,(next==null ? null : (Entry<K,V>) next.clone()));}// Map.Entry Opspublic K getKey() {return key;}public V getValue() {return value;}public V setValue(V value) {if (value == null)throw new NullPointerException();V oldValue = this.value;this.value = value;return oldValue;}public boolean equals(Object o) {if (!(o instanceof Map.Entry))return false;Map.Entry<?,?> e = (Map.Entry<?,?>)o;return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&(value==null ? e.getValue()==null : value.equals(e.getValue()));}public int hashCode() {return hash ^ Objects.hashCode(value);}public String toString() {return key.toString()+"="+value.toString();}}

2.3 相关参数

//记录了存储的元素数量
private transient int count;//元素数量超过这个值后,会执行数组扩容
private int threshold;//负载因子
private float loadFactor;

三、方法

3.1 构造方法

默认构造方法,构造一个大小为11,负载因子为0.75的HashTable

    public Hashtable() {this(11, 0.75f);}

3.2 put

注意Key和Value都不能为空

 //hashtable之所以是线程安全的,就是因为在put和get方法上使用了synchronized关键字进行修饰public synchronized V put(K key, V value) {//限制Value非空if (value == null) {throw new NullPointerException();}// Makes sure the key is not already in the hashtable.Entry<?,?> tab[] = table;//因为直接使用key.hashcode(),而没有像hashmap一样判断key是否是null,所以key为null时,调用key.hashcode()会出错,所以key也不能为空int hash = key.hashCode();//寻址的方式是取余int index = (hash & 0x7FFFFFFF) % tab.length;@SuppressWarnings("unchecked")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;}}//如果table[index]所连接的链表上不存在相同的key,则通过addEntry() 方法将新结点加载到链表的开头addEntry(hash, key, value, index);return null;}

3.2.1 addEntry

private void addEntry(int hash, K key, V value, int index) {modCount++;Entry<?,?> tab[] = table;//如果加入新结点后,hashtable中元素的个数超过了阈值threshold,则利用rehash()对数组进行扩容if (count >= threshold) {// Rehash the table if the threshold is exceededrehash();tab = table;hash = key.hashCode();index = (hash & 0x7FFFFFFF) % tab.length;}//将新结点放在原结点前,相当于把新结点放在table[index]位置,即整个链表的首部@SuppressWarnings("unchecked")Entry<K,V> e = (Entry<K,V>) tab[index];tab[index] = new Entry<>(hash, key, value, e);count++;}

3.2.1.1 rehash

增加数组容量,并重新调整整个hashtable。

    @SuppressWarnings("unchecked")protected void rehash() {int oldCapacity = table.length;Entry<?,?>[] oldMap = table;// overflow-conscious code//每次扩容,容量都变为原来的两倍int 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<?,?>[] 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;}}}

3.3 get

如果存在键值对,那么返回key对应的值,否则返回null,返回的比较结果是通过equals返回的。

    @SuppressWarnings("unchecked")//hashtable线程安全的愿意,synchronized关键字public synchronized V get(Object key) {Entry<?,?> tab[] = table;int hash = key.hashCode();//利用哈希值进行寻址int index = (hash & 0x7FFFFFFF) % tab.length;//遍历table[index]链表,找到key值相同的结点的value并返回,注意这里同样使用了key的equals方法,所以key被应用于hashtable时,不仅要重写hashcode()方法,还要重写equals方法for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {return (V)e.value;}}return null;}

四、Hashtable和HashMap的区别总结

  1. hashmap中key和value均可以为null,但是hashtable中key和value均不能为null。
  2. hashmap采用的是数组(桶位)+链表+红黑树结构实现,而hashtable中采用的是数组(桶位)+链表实现。
  3. hashmap中出现hash冲突时,如果链表节点数小于8时是将新元素加入到链表的末尾,而hashtable中出现hash冲突时采用的是将新元素加入到链表的开头。
  4. hashmap中数组容量的大小要求是2的n次方,如果初始化时不符合要求会进行调整,而hashtable中数组容量的大小可以为任意正整数。
  5. hashmap中的寻址方法采用的是位运算按位与,而hashtable中寻址方式采用的是求余数。
  6. hashmap不是线程安全的,而hashtable是线程安全的,hashtable中的get和put方法均采用了synchronized关键字进行了方法同步。
  7. hashmap中默认容量的大小是16,而hashtable中默认数组容量是11。

HashTable 源码解析 jdk1.8相关推荐

  1. HashMap源码解析(JDK1.8)

    HashMap源码解析(JDK1.8) 目录 定义 构造函数 数据结构 存储实现源码分析 删除操作源码分析 hashMap遍历和异常解析 1. 定义 HashMap实现了Map接口,继承Abstrac ...

  2. hashtable源码解析

    Hashtable 也就是哈希表,是个非常重要的概率,在剖析hashtable源码前,我先简单介绍一下hashtable的原理 哈希表概念 什么是哈希(hash又称散列)? 将任意长度的消息压缩到某一 ...

  3. Java集合-ArrayList源码解析-JDK1.8

    ◆ ArrayList简介 ◆ ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了List, RandomAcc ...

  4. HashMap 源码解析(JDK1.8)

    HashMap是由数组加链表的结合体.如下图: 图中可以看出HashMap底层就是一个数组结构,每个数组中又存储着链表(链表的引用) JDK1.6实现hashmap的方式是采用位桶(数组)+链表的方式 ...

  5. ArrayBlockingQueue源码解析(JDK1.8)

    ArrayBlockingQueue是基于数组的先进先出的有界循环队列 同样我们还是先上类的关系图 从类的关系图中可以看出ArrayBlockingQueue继承一个抽象类和实现了两个接口,然后分别简 ...

  6. 吊打java面试官之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习. 我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable. 第1部分 ...

  7. 源码解析-深刻理解Hash HashTable HashMap原理及数据hash碰撞问题

    HashMap 前言 Hash HashTable 开地址法 线性探测法 平方探测法 双重散列探测法 拉链法 哈希表优势 HashMap 变量介绍 初始容量和负载因子 红黑树和链表转化 HashMap ...

  8. ConcurrentHashMap源码解析——基于JDK1.8

    ConcurrentHashMap源码解析--基于JDK1.8 前言 这篇博客不知道写了多久,总之就是很久,头都炸了.最开始阅读源码时确实是一脸茫然,找不到下手的地方,真是太难了.下面的都是我自己阅读 ...

  9. 万字长文|Hashtable源码深度解析以及与HashMap的区别

    基于JDK1.8对Java中的Hashtable集合的源码进行了深度解析,包括各种方法.扩容机制.哈希算法.遍历方法等方法的底层实现,最后给出了Hashtable和HashMap的详细对比以及使用建议 ...

  10. 万字长文之JDK1.8的LinkedList源码解析

    1.前言 上一篇文章我们看了List集合的数组实现JDK1.8的ArrayList 源码解析,走过路过不要错过,本篇文章我们将介绍 List 集合的链表实现 LinkedList. 2.整体架构 和 ...

最新文章

  1. 硬件巨头正在崛起,中国独占鳌头
  2. python在linux报错xe6,python出现SyntaxError: Non-ASCII character '\xe6' in file \的错误
  3. spring计划任务
  4. ssm配置多数据库支持
  5. 如何把关联性的告警智能添加到 Nagios 上?(2)
  6. php要字符串的后四位,php如何截取字符串后四位
  7. 去掉源代码里的debug标致
  8. 今天,我需要你的支持!
  9. 2021 王道考研 操作系统+习题讲解
  10. vant制作首页的加载中和暂无数据
  11. vs2010中svn使用教程_vs2010+ Ankhsvn使用详解
  12. python立即关机_Python之电脑好帮手—自动定时关机
  13. 快速上手!2021年字节跳动、阿里等大厂最全Android面试题,Android校招面试指南
  14. Nehe教程16课雾
  15. linux查询进程号是否存在启动脚本,Shell实现判断进程是否存在并重新启动脚本分享...
  16. 北邮智能车仿真培训(五)—— 数据可视化工具的使用
  17. 软件测试技术(五)软件测试流程
  18. iOS 通过定位获取常驻后台
  19. 商城项目14_商品新增vo抽取、修改vo、新增逻辑、代码的具体落地、SPU检测、SKU检测、流程图
  20. vivado Clocking_Wizard IP配置

热门文章

  1. java 枚举嵌套枚举_java – 如何使用枚举与分组和分组层次/嵌套
  2. Flink Next:Beyond Stream Processing
  3. OpenGL中 Canvas 性能分析
  4. Android支付实践(三)之银联支付功能(客户端+服务端)
  5. python文件处理——JSON格式文件
  6. php 小程序页面传参,关于微信小程序中页面之间传参的解析
  7. 平板竖屏_朱海舟吐槽iPad办公体验:大量竖屏应用缺乏适配
  8. pythonATM,购物车项目实战2,主函数入口
  9. go代码--数据结构
  10. python股票_python股票 - 随笔分类 - 無碼 - 博客园