HashMap和HashTable简介和区别

1、HashMap简介数组

HashMap是基于哈希表实现的,每个元素是一个key-value对,其内部经过单链表解决冲突问题,容量不足(超过了阀值)时,一样会自动增加。安全

HashMap是非线程安全的,只是用于单线程环境下,多线程环境下能够采用concurrent并发包下的concurrentHashMap。多线程

HashMap 实现了Serializable接口,所以它支持序列化,实现了Cloneable接口,能被克隆。并发

HashMap存数据的过程是:函数

HashMap内部维护了一个存储数据的Entry数组,HashMap采用链表解决冲突,每个Entry本质上是一个单向链表。当准备添加一个key-value对时,首先经过hash(key)方法计算hash值,而后经过indexFor(hash,length)求该key-value对的存储位置,计算方法是先用hash&0x7FFFFFFF后,再对length取模,这就保证每个key-value对都能存入HashMap中,当计算出的位置相同时,因为存入位置是一个链表,则把这个key-value对插入链表头。性能

HashMap中key和value都容许为null。key为null的键值对永远都放在以table[0]为头结点的链表中。spa

了解了数据的存储,那么数据的读取也就很容易就明白了。线程

HashMap的存储结构,以下图所示:code

图片描述

图中,紫色部分即表明哈希表,也称为哈希数组,数组的每一个元素都是一个单链表的头节点,链表是用来解决冲突的,若是不一样的key映射到了数组的同一位置处,就将其放入单链表中。继承

HashMap内存储数据的Entry数组默认是16,若是没有对Entry扩容机制的话,当存储的数据一多,Entry内部的链表会很长,这就失去了HashMap的存储意义了。因此HasnMap内部有本身的扩容机制。HashMap内部有:

变量size,它记录HashMap的底层数组中已用槽的数量;

变量threshold,它是HashMap的阈值,用于判断是否须要调整HashMap的容量(threshold = 容量*加载因子)

变量DEFAULT_LOAD_FACTOR = 0.75f,默认加载因子为0.75

HashMap扩容的条件是:当size大于threshold时,对HashMap进行扩容

扩容是是新建了一个HashMap的底层数组,然后调用transfer方法,将就HashMap的所有元素添加到新的HashMap中(要从新计算元素在新的数组中的索引位置)。 很明显,扩容是一个至关耗时的操做,由于它须要从新计算这些元素在新的数组中的位置并进行复制处理。所以,咱们在用HashMap的时,最好能提早预估下HashMap中元素的个数,这样有助于提升HashMap的性能。

HashMap共有四个构造方法。构造方法中提到了两个很重要的参数:初始容量和加载因子。这两个参数是影响HashMap性能的重要参数,其中容量表示哈希表中槽的数量(即哈希数组的长度),初始容量是建立哈希表时的容量(从构造函数中能够看出,若是不指明,则默认为16),加载因子是哈希表在其容量自动增长以前能够达到多满的一种尺度,当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 resize 操做(即扩容)。

下面说下加载因子,若是加载因子越大,对空间的利用更充分,可是查找效率会下降(链表长度会愈来愈长);若是加载因子过小,那么表中的数据将过于稀疏(不少空间还没用,就开始扩容了),对空间形成严重浪费。若是咱们在构造方法中不指定,则系统默认加载因子为0.75,这是一个比较理想的值,通常状况下咱们是无需修改的。

另外,不管咱们指定的容量为多少,构造方法都会将实际容量设为不小于指定容量的2的次方的一个数,且最大值不能超过2的30次方。

2、Hashtable简介

Hashtable一样是基于哈希表实现的,一样每一个元素是一个key-value对,其内部也是经过单链表解决冲突问题,容量不足(超过了阀值)时,一样会自动增加。

Hashtable也是JDK1.0引入的类,是线程安全的,能用于多线程环境中。

Hashtable一样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。

3、HashMap和Hashtable的区别

一、二者最主要的区别在于Hashtable是线程安全,而HashMap则非线程安全。Hashtable的实现方法里面都添加了synchronized关键字来确保线程同步,所以相对而言HashMap性能会高一些,咱们平时使用时若无特殊需求建议使用HashMap,在多线程环境下若使用HashMap须要使用Collections.synchronizedMap()方法来获取一个线程安全的集合(Collections.synchronizedMap()实现原理是Collections定义了一个SynchronizedMap的内部类,这个类实现了Map接口,在调用方法时使用synchronized来保证线程同步,固然了实际上操做的仍是咱们传入的HashMap实例,简单的说就是Collections.synchronizedMap()方法帮咱们在操做HashMap时自动添加了synchronized来实现线程同步,相似的其它Collections.synchronizedXX方法也是相似原理。

二、HashMap可使用null做为key,不过建议仍是尽可能避免这样使用。HashMap以null做为key时,老是存储在table数组的第一个节点上。而Hashtable则不容许null做为key。

三、HashMap继承了AbstractMap,HashTable继承Dictionary抽象类,二者均实现Map接口。

四、HashMap的初始容量为16,Hashtable初始容量为11,二者的填充因子默认都是0.75。

五、HashMap扩容时是当前容量翻倍即:capacity2,Hashtable扩容时是容量翻倍+1即:capacity2+1。

六、HashMap和Hashtable的底层实现都是数组+链表结构实现。

七、二者计算hash的方法不一样: Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模:

int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;

HashMap计算hash对key的hashcode进行了二次hash,以得到更好的散列值,而后对table数组长度取摸:

static int hash(int h) {
     h ^= (h >>> 20) ^ (h >>> 12);
     return h ^ (h >>> 7) ^ (h >>> 4);
 }

static int indexFor(int h, int length) {
     return h & (length-1);
 }

参考上海尚学堂Java文章,获取更多内容或支持,请点击 上海Java培训

相关文章
相关标签/搜索