1.HashTable的类声明

    public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, java.io.Serializable

继承的是Dictionary类,实现了Map、Cloneable、Serializable。

注意:这也是Hashtable和HashMap第一个不同的地方,HashMap实现的是AbstractMap类。

2. HashTable的成员变量

1)private transient Entry< ?,? >[] table;//用来实现Hashtable所借助的数组,默认大小为11。

注意:这是Hashtable与HashMap第二个不同的地方,Hashtable的默认容量大小为11,HashMap的默认容量大小为16且扩容的大小必须一直是2的幂次方。

2)、private transient int count;//用来记录table数组中存储元素的个数

3)、private int threshold;//扩容的门限,即如果数组table中存储的元素个数大于threshold,则扩大数组table的大小

4)、private float loadFactor;//加载因子,默认值为0.75f,扩容门线threshold的值等于loadFactor与数组table的容量的乘积.

3. HashTable的构造方法

    /**我们获得的hashtable对象,无论有参数,无参数的 最终都调用的这个构造*initialCapacity为初始容量,默认大小为 11*loadFactor为加载因子,默认大小为0.75f,好比是你的内容超过了 总长度了75%,就会自动扩容 */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);if (initialCapacity==0)initialCapacity = 1;this.loadFactor = loadFactor;table = new Entry<?,?>[initialCapacity];//扩容门限threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}

4.HashMap的重要方法

1.put方法

put方法存储数据的思想如下:首先根据拿到key的hashcode,然后根据hashCode找到此元素即将要存储的位置index.如果位置index已经有元素链表了,则在此链表中,寻找是否有key已经存在了,如果存在,则更新value值,如果不存在,则将此value存储在index位置上。

从源码中可以看到,put方法首先检查value是否为null,如果为空,则抛异常。
注意:这也是Hashtable与HashMap的另一个不同点,在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。

另一个区别,在Hashtable中所有的方法都是用synchronized关键字进行了同步,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。

还有区别就是HashMap的teble有可能是treenode结构有可能是列表,而HashTable都是对象列表,所以HashTable处理的相对简单。

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;int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length;@SuppressWarnings("unchecked")Entry<K,V> entry = (Entry<K,V>)tab[index];//如果数组table中有值,则检查下是否有此key,如果有,则更新valuefor(; entry != null ; entry = entry.next) {if ((entry.hash == hash) && entry.key.equals(key)) {V old = entry.value;entry.value = value;return old;}}//如果没有此key,则添加此节点addEntry(hash, key, value, index);return null;}

addEntry方法

思想如下:首先检查下数组table存储的元素个数是否大于扩容门限threshold.如果大于,则扩容,扩容之后,重新获取key的hashcode,并根据hashcode重新计算要存储的位置index.最后将要存储的数据存储到table[index]中。这里要注意的一点是,如果table[index]中已经有其它元素了,那么在同一个位子上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。

   private void addEntry(int hash, K key, V value, int index) {modCount++;Entry<?,?> tab[] = table;//检查数组table中存储的数据个数>=门限,如果大于等于,则对数组进行扩容if (count >= threshold) {// Rehash the table if the threshold is exceededrehash();//扩容之后,对key的hashcode、以及即将要存储的位置index重新进行更新tab = table;hash = key.hashCode();index = (hash & 0x7FFFFFFF) % tab.length;}// Creates the new entry.@SuppressWarnings("unchecked")//获取table数组在index的元素,如果非空,则新节点放在这个节点的前面,成为新的链表头Entry<K,V> e = (Entry<K,V>) tab[index];tab[index] = new Entry<>(hash, key, value, e);count++;}
2、refresh方法

rehash方法的实现思想如下:首先对原来的长度乘以2+1就为即将扩容的长度。然后新建一个newCapacity的数组,将原来的table数组的元素拷贝到新的数组中去即可。

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;@SuppressWarnings("unchecked")protected void rehash() {int oldCapacity = table.length;Entry<?,?>[] oldMap = table;// overflow-conscious codeint 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、get方法

get方法的思想也比较简单,如下:首先获取key的hashcode,然后根据hashcode得到存储位置index,最后在table[index]取出元素即可,不过要注意的是,这里可能存储了多个元素构成了一个链表,因此要进行一个key和hash的判断。

  @SuppressWarnings("unchecked")public synchronized V get(Object key) {Entry<?,?> tab[] = table;int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {return (V)e.value;}}return null;}

**HashMap和Hashtable的几点区别**

1、继承类不一样

HashMap继承的是AbstractMap,Hashtable继承的是Dictionary。实现的接口一致(Map、Cloneable和Serializable)

2、初始容量不一样

HashMap默认容量为16,且容量只能是2的幂次方;Hashtable默认容量为11,容量并没有2的幂次方的限制,增加的方式是 oldCap*2+1。

3、HashMap是线程不安全的,Hashtable是线程安全的

默认情况下,HashMap类中的方法并没有进行同步,而Hashtable中的方法均使用synchronized进行了同步。因此,在多线程并发时,Hashtable可以直接使用,HashMap需要我们加入额外的同步操作。

4、使用的hashcode不一样

Hashtable是直接使用的key的hashcode(key.hashcode())。而HashMap的key的hashcode是另外计算的。hashMap 独立了hash算法,并且算法是通过key value 多次算出来的,减少了重复性

5、HashMap允许有一个key为null,多个value为null。而Hashtable不允许key和value为null。

6、HashMap和Hashtable内部遍历方式的实现不一样

Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。

容器源码分析之HashTable(八)相关推荐

  1. 手机自动化测试:appium源码分析之bootstrap八

    手机自动化测试:appium源码分析之bootstrap八 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大 ...

  2. Spring IOC 容器源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  3. Spring IOC 容器源码分析 - 余下的初始化工作

    1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...

  4. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

  5. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  6. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

  7. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  8. Spring IOC 容器源码分析 - 获取单例 bean

    1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...

  9. Spring IOC 容器源码分析系列文章导读 1

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

最新文章

  1. 《精通 ASP.NET MVC 3 框架(第三版)》----第2章 准备工作 2.1 准备工作站
  2. docker--在centos镜像安装mysql
  3. As Simple as One and Two(思维)
  4. mysql 数据如何存储,MySQL如何存储数据
  5. 数学图形之Boy surface
  6. OJ1050: 阶乘的累加和(C语言实现多重循环和递推方法)
  7. java random array_java復習之Math、Random、Arrays工具類
  8. 19.Qt中Thread线程中创建QTcpSocket
  9. 2020解决连不上GitHub github.com port 443: Timed out
  10. C语言杂谈——指针篇
  11. 设计模式笔录(一),什么是设计模式
  12. 继承求不同形状图形的周长和面积
  13. linux/windows_powershell/bash_硬链接Hardlink/软连接(符号链接)创建以及注意事项/powershell_获取文件绝对路径/linux符号链接检查
  14. numpy.pad对图片进行填充
  15. Python破解压缩包密码
  16. 2022G3锅炉水处理国家题库及答案
  17. 【180622】VC++不规则窗体编程跳棋游戏实例
  18. 22届春招实习试水之路汇总(前端+后端)
  19. 7种抓包工具详细介绍
  20. 抗真菌、细菌化合物库、抑制剂

热门文章

  1. OD的 CC断点,内存访问断点,硬件断点 解析
  2. GetLongPathName和GetShortPathName函数
  3. 秒杀多线程第十一篇 读者写者问题
  4. 秋天的第一份“干货” I Referer 防盗链,为什么少了个字母 R?
  5. Redis从入门到精通,至少要看看这篇!
  6. RabbitMQ之惰性队列(Lazy Queue)
  7. 来自曾经一起“挥洒汗水”的志愿者伙伴们的一封信
  8. linux下多种锁的比较
  9. 2021 有哪些不容错过的后端技术趋势
  10. 腾讯云副总裁邹贤能:将陆续开源四大核心技术