一直写一些工作日常笔记,最近想静下来看下一些常见的知识原理,在很多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源码相关推荐

  1. Java源码详解三:Hashtable源码分析--openjdk java 11源码

    文章目录 注释 哈希算法与映射 线程安全的实现方法 put 操作 get操作 本系列是Java详解,专栏地址:Java源码分析 Hashtable官方文档:Hashtable (Java Platfo ...

  2. hashtable源码解析

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

  3. Java8 Hashtable 源码阅读

    一.Hashtable 概述 Hashtable 底层基于数组与链表实现,通过 synchronized 关键字保证在多线程的环境下仍然可以正常使用.虽然在多线程环境下有了更好的替代者 Concurr ...

  4. 【Java集合源码剖析】Hashtable源码剖析

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/36191279 Hashtable简介 Hashtable同样是基于哈希表实现的,同样每个元 ...

  5. Java集合:Hashtable源码分析

    1. 概述 上次讨论了HashMap的结构,原理和实现,本文来对Map家族的另外一个常用集合HashTable进行介绍.HashTable和HashMap两种集合非常相似,经常被各种面试官问到两者的区 ...

  6. 带你深入浅出的分析 HashTable 源码

    Hashtable 一个元老级的集合类,早在 JDK 1.0 就诞生了,今天小编想和大家一起来揭开它的面纱! 01.摘要 在集合系列的第一章,咱们了解到,Map 的实现类有 HashMap.Linke ...

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

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

  8. HashTable 源码解读

    很多人都知道HashTable与HashMap的关系,HashTable是线程安全的,HashMap是非线程安全的.在介绍完HashMap之后,趁热介绍一下HashTable.在HashTable中没 ...

  9. 【Java源码分析】HashTable源码分析

    类的定义 public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Clon ...

  10. Hashtable源码注释

    2019独角兽企业重金招聘Python工程师标准>>> package java.util; import java.io.*;public class Hashtable<K ...

最新文章

  1. 墨迹天气接口html,moji_weather_spider.py
  2. ocv特性_实际使用工况的锂离子电池SOC-OCV关系
  3. ImportError: cannot import name 'six'解决
  4. 论文浅尝 | 面向单关系事实问题的中文问答模型
  5. html5填空题阅卷,“过五关”般严格,高考阅卷老师来自哪里?研究生参与阅卷?
  6. 爬虫数据传入mysql_nodejs爬虫数据存入mysql
  7. 计算机毕业设计题目汇总 - 简单的毕设选题
  8. mrp手机qq2008下载-MRP格式的手机QQ2008软件介绍及如何正确安装MRPQQ2008挂Q版
  9. 用计算机名共享打印机不能打印,共享打印机无法打印怎么办解决教程
  10. 【精华】PS十大抠图技法(上)
  11. redis 客户端连接及常用命令使用
  12. 回给collapsar的信
  13. 农林牧渔行业S2B2C系统网站提升品牌知名度,提升盈利水平
  14. 经典排序算法学习笔记二——快速排序
  15. 机器学习中类别变量的编码方法总结
  16. 单片机炫彩灯实训报告_51单片机呼吸灯实验报告.doc
  17. 程序自动修复相关工作总结
  18. 软件测试外包的战术有哪些
  19. GroupM小结1(14年8月28日)
  20. 信号与系统(13)- 傅里叶变换的性质

热门文章

  1. 【POJ 1456】Supermarket【两种做法】【二叉堆贪心】【并查集】
  2. JDK11使用HSDB
  3. JAVA的对象创建与调用的内存解析
  4. dao获取到mysql存储函数_GreenDao3.0使用
  5. linux wc -l命令,Linux wc sort和uniq的用法
  6. maven创建web工程,完善目录结构时出现HttpServlet等类的找不到的bug
  7. 最长回文字串--动态规划
  8. COGS 2687 讨厌整除的小明
  9. HDU 1325 Is It A Tree?(并查集)
  10. 在Android中使用自带API操作Json