环境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. Activiti 基本操作之“受理人变量”

    在 Activiti 流程引擎中,尽管通过 setAssignee(taskId, userId) 可以设置受理人,但这毕竟要先把下一步的任务查出来才能设置,比较繁琐:借助 Activiti 的 ac ...

  2. gunicorn geventworker 解析

    在前面的文章曾介绍过gunicorn的syncworker,本文介绍其中一种asyncworker:GeventWorker.类图如下:   可见GeventWorker重载了init_process ...

  3. 【java设计模式】【创建模式Creational Pattern】工厂方法模式Factory Method Pattern(多态性工厂模式Polymorphic Factory Pattern、虚拟构造子模式Virtual Constructor Pattern)

    public class Test { public static void main(String[] args){ Creator ca=new ConcreteCreatorA(); ca.cr ...

  4. lua中易混淆函数

    lua中易混淆的函数 ipairs和pairs: ipairs只能顺序遍历table,遇到key不是数字就会退出 pairs可以遍历table中所有元素 ----------------------- ...

  5. Windows as a Service(4)——使用Intune管理Windows10更新

    这是这个系列的最后一篇文章,我已经花了三篇的篇幅和大家分享有关于Windows as a Serivce的相关内容,链接如下: Windows as a Service(1)-- Windows 10 ...

  6. 类的更新----MVC设计模式

    <?php class stdObject { public function __construct(array $arguments = array()) { if (!empty($arg ...

  7. C# 使用 CancellationTokenSource 终止线程

    http://blog.csdn.net/hezheqiang/article/details/51966511 我们在多线程中通常使用一个 C# 使用 CancellationTokenSource ...

  8. Django ORM详解

    ORM:(在django中,根据代码中的类自动生成数据库的表也叫--code first) ORM:Object Relational Mapping(关系对象映射) 我们写的类表示数据库中的表 我们 ...

  9. 房上的猫:HTML5基础

    一.W3C标准 1)W3C标准不是某一个标准,而是一系列的标准的集合,一个网页主要由三部分组成,即结构(Structure),表现(Presentation)和行为(Behavior) 2)不很严谨的 ...

  10. Python并发实践_03_并发实战之一

    16S数据质控流程,一次下机lane包括很多的项目,每个项目有独立的合同号,一个项目可能包含16S或者ITS两种,通过一个完整的pipeline,将上游拆分好的数据全部整理成可以直接分析的数据.原本这 ...