一、概述

HashMap和Hashtable的区别在面试的时候经常会被问到,那么它们有什么区别呢?这里谈一下它们各自的特点以及它们的区别在哪里。

二、HashMap

1、HashMap是键值对key-value形式双列集合。它的底层存储原理是哈希表。为了简明描述哈希表(数组+链表),我画了一个图(不专业,轻喷)。

2、对应HashMap采用哈希表存储键值对元素的方式, 配合着上图做一些说明:

1)E*代表一个Node节点,每个Node节点就是我们理解的一个key-value的mapping映射。

2)每个Node除了保存了key和value的映射外呢,还保存了它下一Node的引用。例如图中,Eb保存了Ebb

的引用,而Ebb保存了Ebbb的引用。

3)HashMap的put(key,value)方法介绍

每一个链表,如Eb-->Ebb-->Ebbb,这三个节点的key是不相等的。那么你可能会问,为什么它们三个会被放

在一个链表中呢?是这样的。当你调用put(key,value)方法时,会根据key计算出一个hash值,然后再通

过这个hash值和map当前的长度计算出一个数值,然后将这个数值作为图中数组tab脚标而获取这个脚标

对应的Node节点。如果这个节点不存在,则直接创建一个新的Node节点,插入到数组中的你计算出的那个

脚标的位置。如果存在,则判断key和你put进来的key是否相等(注意相等的判定:hash值相等且equal

相等),如果相等,那么直接更新其值value,也就是我们常见的覆盖旧value操作,如果旧value为null,则

直接将null值设置为你传进来的value值,如果不相等(此处不介绍LinkedHashMap)则去以遍历的方式寻

找这个节点的next节点,如果和这个链表的每个节点的next节点都不相等,则在链表的最后一个Node节点

后创建新节点。如果其中判定有一个相等,那么进行覆盖值并返回旧值操作。当然,这只是put方法的概要

解读,更详细的解读需要自己看一下源码。

HashMap.put(key,value)方法的注意点:

(1)没有synchronized关键字修饰,意味着它是非线程安全的。

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}

(2)key和value可以是null-null、null-value、key-null的形式。如果Map中不存在将要添加的元素那么返

回值为null,如果已经存在且value不为null,那么新value覆盖旧value,返回旧value,如果旧value为null,

那么将新value和key形成一个key-value映射,并返回null。key为null的时候,null也是一个键,键具有唯

一性,value可以重复。

3、HashMap的resize()方法理解

这个方法时HashMap容量不够时进行扩容的方法,觉得有必要说一下。

当HashMap的容量不够用时,再往容器中添加元素时,HashMap会进行扩容操作。当HashMap的容量为最大的

时,则不扩容,但是容器阈值会设置为Integer类型的最大值。当不是最大的时,容器会进行扩容,容量会变为

原来的二倍(此时也不大于最大容量),并且阈值也会随之变化,变为原阈值的二倍。扩容就是在堆中新

创建一个HashMap容器,然后将原来就的HashMap中的元素放到新容器中,放置的时候会重新计算每个Node节

点在哈希表中的位置。

4、HashMap的构造

我们常用的都是空参构造。空参构造一个HashMap,那么它的构造方法使用的是默认的初始化容量16和

加载因子0.75。

    /*** Constructs an empty <tt>HashMap</tt> with the default initial capacity* (16) and the default load factor (0.75).*/public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted}

当我们使用带参构造:

    /*** Constructs an empty <tt>HashMap</tt> with the specified initial* capacity and load factor.** @param  initialCapacity the initial capacity* @param  loadFactor      the load factor* @throws IllegalArgumentException if the initial capacity is negative*         or the load factor is nonpositive*/public HashMap(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);}

此时,阈值(threshold )也不是简单的容量*加载因子获取,而是需要通过一个算法,如下:

this.threshold = tableSizeFor(initialCapacity);

tableSizeFor(initialCapacity):

    /*** Returns a power of two size for the given target capacity.* 根据给定的目标容量,返回一个2的整数倍的数(自己翻译,水平有限,轻喷)。*/static final int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;}

这里探讨这个构造的本意在于,如果我们能预估出Map大概能存储多个少键值对,那么我们可以直接通过指

定容量这种带参构造穿件Map实例,这样可以避免Map的扩容造成的资源和性能浪费。对应加载因子0.75的

默认值,我们一般是不做修改的,这涉及到Map的存取性能问题。

HashMap是非线程安全的,是因为HashMap的方法都是没有用synchronized关键字修饰的。

三、Hashtable

Hashtable已经被弃用的一个类,性能比较低,它有一些自己的特点,不知道你发现没有,它不符合大小驼

峰命名规则,这点很讨厌。

1、Hashtable的方法几乎都是同步的,都有synchronized关键字修饰,因此和HashMap相比,它是线程安全的。

public synchronized V put(K key, V value) {...}

2、Hashtable中key-value的映射,key和value 都是不允许为null的,如果为null了呢?对不起,空指针异常抛出。

3、Hashtable在计算节点元素在哈希表中的位置使用的算法稍有区别,它有它的好处,但和HashMap的算法比

起来明显性能低一些。

4、Hashtable的扩容是原来容量的二倍加1(2n+1),源码参考:int newCapacity = (oldCapacity << 1) + 1;

四、HashMap和Hashtable有哪些主要区别呢?

1、HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。

2、Hashtable比HashMap多提供了elments() 和contains() 两个方法。

3、HashMap的key-value支持key-value,null-null,key-null,null-value四种。而Hashtable只支持key-value一种(即

key和value都不为null这种形式)。既然HashMap支持带有null的形式,那么在HashMap中不能由get()方法来判断

HashMap中是否存在某个键, 而应该用containsKey()方法来判断,因为使用get的时候,当返回null时,你无法判断到底

是不存在这个key,还是这个key就是null,还是key存在但value是null。

4、线程安全性不同,HashMap的方法都没有使用synchronized关键字修饰,都是非线程安全的,而Hashtable的方法几乎

都是被synchronized关键字修饰的。但是,当我们需要HashMap是线程安全的时,怎么办呢?我们可以通过Collections.synchronizedMap(hashMap)来进行处理,亦或者我们使用线程安全的ConcurrentHashMap。ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。

5、初始容量大小和每次扩充容量大小的不同 
Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。

6、计算hash值的方法不同 
为了得到元素的位置,首先需要根据元素的 KEY计算出一个hash值,然后再用这个hash值来计算得到最终的位置。

Hashtable直接使用对象的hashCode。hashCode是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值。然后再使用除留余数发来获得最终的位置。 

Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的。 
HashMap为了提高计算效率,将哈希表的大小固定为了2的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。

HashMap的效率虽然提高了,但是hash冲突却也增加了。因为它得出的hash值的低位相同的概率比较高,而计算位运算

为了解决这个问题,HashMap重新根据hashcode计算hash值后,又对hash值做了一些运算来打散数据。使得取得的位置更加分散,从而减少了hash冲突。当然了,为了高效,HashMap只做了一些简单的位处理。从而不至于把使用2 的幂次方带来的效率提升给抵消掉。

本文参考了以下内容:https://blog.csdn.net/wangxing233/article/details/79452946

HashMap和Hashtable理解与对比相关推荐

  1. 对比分析HashMap,HashTable,ConcurrentHashMap,LinkedHashMap,LURLinkedHashMap(一)

    前言: 这次写几篇 关于 HashMap,HashTable,ConcurrentHashMap,LinkedHashMap,LURLinkedHashMap 源码分析. 如果直接将他们源码,并不好理 ...

  2. 深度理解 HashMap 和 Hashtable 的区别

    这只是基础: HashMap 和 Hashtable 都实现了 Map 接口,因此很多特性非常相似.但是,他们有以下不同点: HashMap 允许键和值是 null,而 Hashtable 不允许键或 ...

  3. 一文读懂JDK7,8,JD9的HashMap,HashTable,ConcurrentHashMap及他们的区别

    内容和标题一样长哦,人家写了好久的.如无特别指明,内容对应的源码是jdk1.7(后面会和1.8对比) 1:hashmap简介(如下,数组-链表形式) HashMap的存储结构 图中,紫色部分即代表哈希 ...

  4. Java集合——HashMap、HashTable以及ConCurrentHashMap异同比较

    转发:https://www.cnblogs.com/zx-bob-123/archive/2017/12/26/8118074.html 0. 前言 HashMap和HashTable的区别一种比较 ...

  5. JAVA面试题:HashMap和Hashtable的区别

    HashMap和Hashtable的区别 1.共同点:都是双列集合,底层都是哈希算法 2.区别: * 1.HashMap是线程不安全的,效率高,JDK1.2版本 * Hashtable是线程安全的,效 ...

  6. 3、HashMap、HashTable和ConcurrentHashMap的区别?

    HashMap和HashTable的区别一种比较简单的回答是: (1)HashMap是非线程安全的,HashTable是线程安全的. (2)HashMap的键和值都允许有null存在,而HashTab ...

  7. HashMap、HashTable和ConcurrentHashMap的区别?

    HashMap和HashTable的区别一种比较简单的回答是: (1)HashMap是非线程安全的,HashTable是线程安全的. (2)HashMap的键和值都允许有null存在,而HashTab ...

  8. java HashMap 与HashTable的区别

    HashMap 与HashTable的区别 HashMap与Hashtable的区别是面试中经常遇到的一个问题.这个问题看似简单,但如果深究进去,也能了解到不少知识.本文对两者从来源,特性,算法等多个 ...

  9. HashMap、HashTable、ConcurrentHashMap、HashSet区别 线程安全类

    HashMap专题:HashMap的实现原理--链表散列 HashTable专题:Hashtable数据存储结构-遍历规则,Hash类型的复杂度为啥都是O(1)-源码分析 Hash,Tree数据结构时 ...

最新文章

  1. C++ 多线程:条件变量 std::condition_variable
  2. ORACLE时间函数(SYSDATE)深入理解
  3. 11.6 如何使用内嵌资源类(ResourceRetriever)?
  4. VTK:简单操作之GaussianRandomNumber
  5. C++string中find_first_not_of()函数和find_last_not_of()函数
  6. 【性能优化】 之性能视图及性能参数
  7. 硬件:U盘无法识别的解决方案
  8. 报错,但不影响运行ERROR: JDWP Unable to get JNI 1.2 environment, jvm-GetEnv() return code = -2...
  9. php 字符串 替换 最后,php如何替换字符串中的最后一个字符
  10. linux笔记索引 QQFF-200305
  11. 分布式和微服务_太难了!阿里三面凉透~ Spring+高并发+算法+分布式微服务等等一个都没讲不清...
  12. pygame判断鼠标左键_鼠标科普,选对鼠标用好鼠标。
  13. SD/TF卡驱动(一)--------SD卡相关简介
  14. Java基础学习之函数式编程Comsumer接口(JDK8)
  15. Nginx关闭后,网页仍能访问(缓存问题)
  16. C语言学习1——第一、二、三章学习记录
  17. 用html和css设计QQ注册页面,html和css制作QQ企鹅教程
  18. OAuth client 微信、qq、新浪登录
  19. CTP报单交易指令(一)限价单
  20. 织梦 php 调用栏目,织梦dedecms如何调用当前栏目文章数

热门文章

  1. window的pageX,pageY,screenX,sreenY,clientX,clientY的区别描述
  2. iOS svn 冲突解决
  3. 考研英语——刷题看课流程
  4. Linux学习笔记六:SSH 软件安装失败了:Server responded Protocol error packet too long 1349676920
  5. linux添加用户和用户组
  6. golang 面试题(从基础到高级)
  7. 机器人学Robotics学习资料 | 我的SLAM入门路线分享
  8. Camtasia处理声音与画面的分离编辑
  9. SPSS工具:时间序列分析---商业销量预测
  10. python 爬取免费音乐