2019独角兽企业重金招聘Python工程师标准>>>

1. 类定义

这个从源码中可以直接看出来,HashMap 继承自 AbstractMap,而 Hashtabl 继承自 Dictionary。

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

2. 线程安全性

Hashtable 在很多方法定义时都会加上 synchronized 关键字,说明 Hashtabl 是线程安全的,而 HashMap 并不能保证线程安全。

public synchronized int size();
public synchronized boolean isEmpty();
public synchronized boolean contains(Object o);
public synchronized V get(Object key);
public synchronized V put(K key, V value);
...

3. key 和 value 是否允许 null

在 Hashtable 添加元素源码中,我们可以发现,如果添加元素的 value 为 null 时,会抛出 NullPointerException。在程序内部,有这样一行代码 int hash = key.hashCode ,如果添加的 key 为 null 时,此时也会抛出空指针异常,因此,在 Hashtable 中,是不允许 key 和 value 为 null 的。

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

而在 HashMap 的 put 方法中,调用了 putVal 方法(1.8 版本中),该方法需要有一个 int 类型的 hash 值,这个值是利用内部的 hash 方法产生的。从下面的源代码可以看出,当 key 为 null 时,返回的 hash 值为 0,说明在 HashMap 中是允许 key=null 的情况存在的。

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict){
}static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

4. 是否提供 contains 方法

从 HashMap 的 API 可以看出,它只包含 containsKey 和 containsValue 方法。而 Hashtable 还包含了 contains 方法。

HashMap 中把 contains 方法去掉的原因主要它容易引起混淆,不如 containsKey 和 containsValue 表达的准确。

而 Hashtable 中 contains 方法也是调用 containsKey 方法来实现的。

public boolean contains(Object o) {return containsKey(o);
}

5. 初始容量

Hashtable 初始容量为 11,默认的负载因子为 0,.75。HashMap 定义了两个常量在对容器进行初始化会用到,可以看到其初始容量为 16,默认的负载因子也是为 0.75.

//---------------------Hashtable-----------------------------
public Hashtable() {this(11, 0.75f);
}
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);
}
//---------------------HashMap-----------------------------
/*** The default initial capacity - MUST be a power of two.*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/*** The load factor used when none specified in constructor.*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;

6. 扩容程度

Hashtable 扩容时调用 rehash 方法,增加容器容量的代码在下面。从中可以看出,最终的容量是 newCapacity ,如果该变量在没有大于 MAX_ARRAY_SIZE(静态变量,内部定义为 Integer.MAX_VALUE - 8) 之前,都是按照 oldCapacity*2 + 1 的速度增加的。

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];

在 HashMap 中,扩容主要是通过 resize 方法实现的,其扩容的代码是这样的 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];,可见是跟 newCap 变量有关,在正常情况下,newCapa 是按照 oldCap<<1 的速度,即每次长度变为原来的两倍增长的。

 if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;
else {               // zero initial threshold signifies using defaultsnewCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}

7. 迭代器

本段话摘自 http://www.cnblogs.com/alexlo/archive/2013/03/14/2959233.html

HashMap 的迭代器(Iterator) 是 fail-fast 迭代器,而 Hashtable 的 enumerator 迭代器不是 fail-fast 的。所以当有其它线程改变了 HashMap 的结构(增加或者移除元素),将会抛出 ConcurrentModificationException,但迭代器本身的 remove() 方法移除元素则不会抛出 ConcurrentModificationException 异常。但这并不是一个一定发生的行为,要看 JVM 。这条同样也是 Enumeration 和 Iterator 的区别。关于 fail-fast 机制可以查看这篇文章。

8. hash 算法

一下下这段分析摘自:https://www.cnblogs.com/xiaoxi/p/7233201.html hash 算法是将元素定位到相对应桶的位置上,在 Hashtable 中,是这样实现 hash 算法的。因为 Hashtable 中,其桶扩容之后长度为奇数,这种方式的哈希取模会更加均匀(这点还是不清楚为什么)。

int hash = key.hashCode();
// hash 不能超过 Integer.MAX_VALUE,所以要取其最小的 32 个 bit
int index = (hash & 0x7FFFFFFF) % tab.length;

在 JDK 1.8 版本中,HashMap 的 hash 方法如下。

static final int hash(Object key){int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}final V putValu(int hash,K key,V value,boolean onlyIfAbsent,boolean evict){...if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);...
}
  • 其首先是获取该对象的 hashCode 值,然后将 hashCode 值右移 16 位,然后将右移后的值与原来的 hashCode 做 异或 运算,返回结果。
  • 在 putVal 中,通过 (n-1)&hash 获取该对象的键在 hashmap 中的位置。(其中 hash 就是通过 hash 方法获得的值),n 表示 hash 桶数组的长度,并且该长度为 2 的 n 次方。

通常声明 map 集合时不会指定大小,或者初始化的时候就创建一个容量很大的map 对象,所以这个通过容量大小与 key 值进行 hash 的算法在开始的时候只会对低位进行计算,虽然容量的 2 进制高位一开始都是 0,但是 key 的 2 进制高位通常是有值的,因此先在 hash 方法中将 key 的 hashCode 右移 16 位在与自身异或,使得高位也可以参与hash,更大程度上减少了碰撞率。

转载于:https://my.oschina.net/firepation/blog/1924879

Hashtable 和 HashMap 的区别相关推荐

  1. HashTable和HashMap的区别详解

    HashTable和HashMap的区别详解 一.HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同 ...

  2. HashTable和HashMap的区别(网上整理)

    1.hashtable是继承自陈旧的Dictionary类的,而hashmap继承自AbstractMap类的同时对Java1.2引进的Map接口进行了实现. 2.hashtable的方法是同步的,而 ...

  3. 五、Hashtable与HashMap的区别

    HashMap不是线程安全的,HashTable是线程安全.HashMap允许空(null)的键和值(key),HashTable则不允许.HashMap性能优于Hashtable. Map 1.Ma ...

  4. HashTable和HashMap的区别

    转载: http://www.importnew.com/24822.html 1.时间 2. 作者 以下是HashTable的作者: 1 2 3 4 5 以下代码及注释来自java.util.Has ...

  5. Hashtable和HashMap的区别:

    1.Hashtable是Dictionary的子类,HashMap是Map接口的一个实现类: 2.Hashtable中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的.即是说,在多线程应 ...

  6. 【转】HashTable 和 HashMap的区别

    这个问题是阿里巴巴面试的时候遇到的.说到底兜了半天,他还是想问线程安全问题...我擦,我没那方面经验. from:http://java.ccidnet.com/art/297/20060428/53 ...

  7. java基础之HashTable和HashMap的区别

    1.类继承关系 public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cl ...

  8. hashtable和hashmap的区别?

    相同点: 1.都实现了map接口 2.都是键值对的方式存储 3.都是通过单链表解决冲突的 4. 都实现了Serializable接口和Cloneable接口,因此它支持序列化和被克隆. 不同点: 1. ...

  9. hashmap原理_HashMap和HashTable底层原理以及区别

    HashMap底层原理 哈希表:在哈希表中进行添加,删除,查找等操作,性能十分之高,不考虑哈希冲突的情况下,仅需一次定位即可完成,时间复杂度为O(1). 数据结构的物理存储结构只有两种:顺序存储结构和 ...

最新文章

  1. 面向对象编程的思想(2)
  2. 教你从0到1搭建秒杀系统-限流
  3. php模拟post提交请求与调用接口
  4. 开源纯C#工控网关+组态软件(六)图元组件
  5. 向量空间 Vector Space -- 推荐系统
  6. 在Docker上快速配置PerconaXtraDBCluster集群
  7. pcb二次钻孔_作为一名合格的PCB设计工程师,了解生产制造很重要
  8. 印刷质量缺陷的视觉检测原理概述
  9. Java爬取酷狗音乐歌单
  10. 电路交换,报文交换和分组交换的区别?
  11. 无线路由器与交换机配合使用,图解
  12. The DAO事件始末
  13. 宽松委托转换(Relaxed delegate conversion)
  14. 服务器装win7没有硬盘分区,深度win7安装没有磁盘分区怎么办?
  15. Sql server2008的使用
  16. CCRC信息安全服务资质。
  17. 用支付宝和微信可以直接跳转拉起支付,API搭建比较方便第三方支付接口首选杉德比较靠谱,
  18. 关于Django字段类型中 blank和null的区别
  19. RuntimeError: NEOS requires a valid email address. Please set the ‘NEOS_EMAIL‘ environment variable.
  20. [转]XML详解--Schema

热门文章

  1. GB-T 16260.1-2006 软件工程 产品质量
  2. C#性能优化考虑的几个方向
  3. SDUT 1149 勾股定理第一弹 勾股数
  4. 加深认识与理解ADO.NET
  5. asp.net ajax实现在线人员的显示
  6. Logstash配置语法
  7. 接口测试--自定义断言设置
  8. 性能测试之JMeter配置元件【随机变量】
  9. linux脚本生成数字写入文本,4.2 编写Shell脚本(P80-85)——《Linux就该这么学》学习笔记16...
  10. eplan备份时卡顿_EPLAN卡顿了怎么办?