HashMap、Hashtable 以及HashSet

HashMap、Hashtable 以及HashSet

    关于多线程的问题大多会涉及到Collection框架,涉及到Collection框架就不得不谈HashSet和HashMap。HashMap和HashSet都是collection框架的一部分,它们让我们能够使用对象的集合。collection框架有自己的接口和实现,主要分为Set接口,List接口和Queue接口。它们有各自的特点,Set的集合里不允许对象有重复的值,List允许有重复,它对集合中的对象进行索引,Queue的工作原理是FCFS算法(First Come, First Serve)。

    首先让我们来看看什么是HashMap和HashSet,HashTable,然后再来比较它们之间的分别。使用它们之前,先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。

什么是HashSet?

    HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。如果我们没有重写这两个方法,将会使用这个方法的默认实现。

public boolean add(Object o)方法用来在Set中添加元素,当元素值重复时则会立即返回false,如果成功添加的话会返回true。

什么是HashMap?

    HashMap实现了Map接口,Map接口对键值对进行映射。Map中不允许重复的键。Map接口有两个基本的实现,HashMap和TreeMap。TreeMap保存了对象的排列次序,而HashMap则不能。HashMap允许键和值为null。HashMap是非synchronized的,但collection框架提供方法能保证HashMap synchronized,这样多个线程同时访问HashMap时,能保证只有一个线程更改Map。

public Object put(Object Key,Object value)方法用来将元素添加到map中。同时HashMap不能保证随着时间的推移Map中的元素次序是不变的

什么是HashTable?

    Hashtable是个过时的集合类,存在于Java API中很久了。在Java 4中被重写了,实现了Map接口,所以自此以后也成了Java集合框架中的一部分,Hashtable与HashMap几乎相同,除了HashMap是非synchronized的,并且HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

三者之间的简单比较

 

HashSet

HashMap

HashTable

实现接口

实现了Set接口

实现了Map接口

同HashMap

元素存储方式

HashSet仅仅存储对象

HashMap储存键值对

同HashMap

新增元素方式

使用add()方法将元素放入set中

使用put()方法将元素放入map中

同HashMap

元素比较

HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false

HashMap中使用键对象来计算hashcode值

 

同HashMap

性能(一般情况)

HashSet较HashMap来说比较慢,具体原因可以从元素比较的步骤得知。

HashMap比较快,因为是使用唯一的键来获取对象

 

 

线程安全

 

是,Hashtable是线程安全的也是synchronized

 

一些重要的知识:

1) sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。

2) Fail-safe和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。

3) 结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构。

 

对于多线程环境中应用HashMap,主要有以下几种选择:

  1. 使用线程安全的java.util.Hashtable作为替代。
  2. 使用java.util.Collections.synchronizedMap方法,将已有的HashMap对象包装为线程安全的。
  3. 使用java.util.concurrent.ConcurrentHashMap类作为替代,它具有非常好的性能。

 

结论:

使用哪种数据结构可以考虑几个问题:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。

 

参考

《无锁HashMap的原理和实现》http://coolshell.cn/articles/9703.html

《HashMap的工作原理》http://www.importnew.com/7099.html

《HashMap和HashSet的区别》http://www.importnew.com/6931.html

《HashMap和HashTable的区别》http://www.importnew.com/7010.html

 

图片出处 堆糖网 原始链接地址:http://www.duitang.com/people/mblog/120757698/detail/