环境jdk1.8.0_121

与HashMap有几点区别(不了解HashMap的具体实现,看我另个博客http://www.cnblogs.com/dj3839/p/8111675.html

在HashMap中,冲突的值会在bucket形成链表,当达到8个,会形成红黑树,而在HashTable中,冲突的值就以链表的形式存储

    public synchronized V put(K key, V value) {
// Make sure the value is not null
if (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];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
} addEntry(hash, key, value, index);
return null;
}

会发现求索引的方式也不一样,(hash&0x7FFFFFFF)%tab.length,而在HashMap中是(hash^(hash>>>16))&(tab.length-1),可以看出HashTable里,并没有做出相应的优化,这边解释下HashMap中的优化,(hash^(hash>>>16))这一步是其实是让一个hash值的高16位和低16位做异或,混合高位和低位,加大低位的随机性,(hash^(hash>>>16))&(tab.length-1)求与,其实就是相当于HashTable中的取模,只是在你计算机中用位预算效率比较高,当然tab.length在HashMap中其实是一个2的n次方,所以能达到这一的效果。

还有一点,可以看到HashTable中是不允许放值为Null的value,它会抛出错误。而且key值也不能为null,因为它直接拿key.hashCode(),null是拿不到hashCode也会发生错误。

继续看addEntry,开始添加元素

    private void addEntry(int hash, K key, V value, int index) {
modCount++; Entry<?,?> tab[] = table;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
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++;
}

代码非常简洁,如果数量大于限定值,就开始扩充,重新计算索引位置,然后插入

先看插入

tab[index] = new Entry<>(hash, key, value, e);

在创建entry的时候,传了个bucket的第一个entry,

        protected Entry(int hash, K key, V value, Entry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}

看构造函数其实可以看出,在这里进行指向旧的第一个entry,因此,在hashtable中其实是插入在链表的头,而在HashMap是在尾

然后我们在看它的rehash

    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 buckets
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;
}
}
}

比HashMap简单的多。。。len扩充2*len+1,然后对原来bucket中的entry重新计算索引,并赋值,不改变链表原先的顺序,在HashMap中复杂的多,可以看我另个讲HashMap的博客

而且在hashtable中,调用构造函数时,直接初始化了里面的数组table,而在hashmap中是在进行put操作时,进行初始化,这个操作也在resize中

可以看下HashTable中的初始化方法

    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);
}

还有一点,HashTable它的初始化,默认容量len是为11,后面也是2*len+1扩充,而HashMap是16,以后的扩充数量都是len*2,并且我们提供容量大小时,也是会转成一个2的n次方,为什么会有这样的区分,和它计算hash有关,在前面提到了(hash^(hash>>>16))&(tab.length-1),2^n-1,二进制就是n个1

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

但相对HashMap,HashTable是线程安全的,因为在很多方法,比如get,put,equals等,都使用了synchronized同步锁。

总结:

1. HashTable的key、value不能为null

2. HashTable线程安全

3. HashTable的优化其实没有HashMap做的好,在单线程的情况,最好使用HashMap

贴上一句源码中的提示

 * Java Collections Framework</a>.  Unlike the new collection
* implementations, {@code Hashtable} is synchronized. If a
* thread-safe implementation is not needed, it is recommended to use
* {@link HashMap} in place of {@code Hashtable}. If a thread-safe
* highly-concurrent implementation is desired, then it is recommended
* to use {@link java.util.concurrent.ConcurrentHashMap} in place of
* {@code Hashtable}.

大致意思就是:不需要线程安全用HashMap,需要线程安全的高并发用ConcurrentHashMap

HashTable源码阅读的更多相关文章

  1. 13 HashTable抽象哈希表类——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...

  2. java源码阅读Hashtable

    1类签名与注释 public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, C ...

  3. HashMap与HashTable的哈希算法——JDK1.9源码阅读总结

    下面是HashTable源码中的put方法: 注意上面注释标注的地方: HashTable对于元素在哈希表中的坐标算法是: 将对象自身的哈希值key.hashCode()变为正数:hash & ...

  4. SparkConf加载与SparkContext创建(源码阅读一)

    即日起开始spark源码阅读之旅,这个过程是相当痛苦的,也许有大量的看不懂,但是每天一个方法,一点点看,相信总归会有极大地提高的.那么下面开始: 创建sparkConf对象,那么究竟它干了什么了类,从 ...

  5. java8 ArrayList源码阅读

    转载自 java8 ArrayList源码阅读 本文基于jdk1.8 JavaCollection库中有三类:List,Queue,Set 其中List,有三个子实现类:ArrayList,Vecto ...

  6. 34 网络相关函数(二)——live555源码阅读(四)网络

    34 网络相关函数(二)——live555源码阅读(四)网络 34 网络相关函数(二)——live555源码阅读(四)网络 2)socketErr 套接口错误 3)groupsockPriv函数 4) ...

  7. 32 GroupSock(AddressPortLookupTable)——live555源码阅读(四)网络

    32 GroupSock(AddressPortLookupTable)——live555源码阅读(四)网络 32 GroupSock(AddressPortLookupTable)——live555 ...

  8. 15 BasicHashTable基本哈希表类(二)——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...

  9. 14 BasicHashTable基本哈希表类(一)——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 http://www.cnblogs.com/oloroso ...

随机推荐

  1. Struts2中validate数据校验的两种常用方法

    本文主要介绍Struts2中validate数据校验的两种方法及Struts2常用校验器.  1.Action中的validate()方法 Struts2提供了一个Validateable接口,这个接 ...

  2. 服务器固件测试--PCI设备的介绍(集成网卡和外插网卡)

    今天2017年9月26号,快三个月的时间,是该梳理一下,我来到这个岗位学到的东西. 网卡是什么 网卡分为俩大类 板载的集成网卡和外插的网卡.外插的网卡又分为很多种. 板载的集成网卡 外插的网卡分为 I ...

  3. 学习Git的最佳资料

    1. ProGit中文版:https://git-scm.com/book/zh/v2 2. 廖雪峰的Git教程: http://www.liaoxuefeng.com/wiki/0013739516 ...

  4. IDEA引MAVEN项目jar包依赖导入问题解决

    Intellj 自动载入Mave依赖的功能很好用,但有时候会碰到问题,导致pom文件修改却没有触发自动重新载入的动作,此时需要手动强制更新依赖. 如下: 1手动删除Project Settings里面 ...

  5. java与数据库

    工具:mysql: java eclipse,phpstudy. 以MySQL为例 java连接MySQL可能你在度娘的帮助下,又设置环境变量又改这改那的,结果还是没有连接成功. 今天我来分享一下不需 ...

  6. 【深度学习系列】用PaddlePaddle和Tensorflow实现GoogLeNet InceptionV2/V3/V4

    上一篇文章我们引出了GoogLeNet InceptionV1的网络结构,这篇文章中我们会详细讲到Inception V2/V3/V4的发展历程以及它们的网络结构和亮点. GoogLeNet Ince ...

  7. Lua中使用状态机FSM简单例子

    FSM 有限状态机: 一个有限状态机是一个设备,或者是一个设备模型,具有有限数量的状态,它可以在任何给定的时间根据输入进行操作,使得一个状态变换到另一个状态,或者是使一个输入或者一种行为的发生.一个有 ...

  8. Huffman 哈夫曼编码与译码的原理剖析及C++实现

    原理 我们在信息存储时,希望以最少的空间去存储最大的数据,方便数据的传输,那么该怎样做呢? 我们想到将源信息转化为01序列存储,但是这样以来又有一个问题,就是子串匹配问题,我们为了解决这个方法,想到了 ...

  9. UILabletext去掉乱码 控制颜色 行高 自定义大小 。显示不同的字体颜色、字体大小、行间距、首行缩进、下划线等属性(NSMutableAttributedString)

    text去掉乱码 设置不同颜色 行高 自定义大小 #import <Foundation/Foundation.h> @interface TextsForRow : NSObject @ ...

  10. JAVA 解析、编辑nginx.conf

    最近工程开发遇到一个需求:用Java去解析并编辑nginx.conf 在github上找到nginx-java-parser工具,项目地址:https://github.com/odiszapc/ng ...