1、HashTable与HashMap

(1)HashTable和HashMap都实现了Map接口,但是HashTable的实现是基于Dictionary抽象类。

(2)在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,既可以表示HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。而在HashTable中,无论是key还是value都不能为null。

(3)HashTable是线程安全的,它的方法是同步了的,可以直接用在多线程环境中。HashMap则不是线程安全的。在多线程环境中,需要手动实现同步机制。

因此,在Collections类中提供了一个方法返回一个同步版本的HashMap用于多线程的环境:

public static <K, V> Map<K, V> synchronizedMap(Map<K, V> m) {
return new SynchronizedMap<K, V>(m);
}

该方法返回的是一个SynchronizedMap的实例。SynchronizedMap类是定义在Collections中的一个静态内部类。它实现了Map接口,并对其中的每一个方法实现,通过synchronized关键字进行了同步控制。

2、潜在的线程安全问题

上面提到的Collections为HashMap提供了一个并发版本的SynchronizedMap。这个版本中的方法都进行了同步,但是这并不等于这个类就一定是线程安全的。在某些情况下会出现一些意想不到的结果。

如下面这段代码:

// shm是SynchronizedMap的一个实例
if(shm.containsKey(key)) {
shm.remove(key)
}

这段代码用于从map中删除一个元素之前判断是否存在这个元素。这里的containsKey()和remove()方法都是同步的,但是整段代码却不是。

考虑这么一个场景:线程A执行了containsKey()方法返回true,准备执行remove()操作;这时另一个线程B开始执行,同样执行了containsKey()方法返回true,并接着执行了remove()操作;然后线程A接着执行remove操作时发现此时已经没有这个元素了。

要保证上面这个场景下代码按我们的医院工作,一个办法就是对这段代码进行同步控制,但是这付出的代价太大了。在进行迭代时这个问题更加明显。

Map集合共提供了三种方式来分别返回键、值、键值对的集合:

Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();

在这三个方法基础上,我们一般通过如下方式访问Map的元素:

Iterator keys = map.keySet().iterator();  

while(keys.hasNext()) {
map.get(keys.next());
}

在这里,有一个地方需要注意的是:得到的keySet和迭代器都是Map中元素的一个“视图”,而不是“副本”。问题也就出现在这里,当一个线程正在迭代Map中的元素时,另一个线程可能正在修改其中的元素。此时,在迭代元素时就可能抛出ConcurrentModificationException异常。

为了解决这个问题,通常有两种方法:

(1)一是直接返回元素的副本,而不是视图。这个可以通过集合类的toArray()方法实现,但是创建副本的方式效率比之前有所降低,特别是在元素很多的情况下;

(2)另一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了。

3、更好的选择:ConcurrentHashMap

Java 5中新增了ConcurrentMap接口和它的实现类ConcurrentHashMap。

ConcurrentHashMap提供了和HashTable以及SynchronizedMap中所不同的锁机制。HashTable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能有一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。

上面说到的16个线程指的是写线程,而读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表。

在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是,在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据,这样iterator可以使用原来老的数据,而写线程也可以并发的完成改变。

HashMap、HashTable与ConcurrentHashMap的区别的更多相关文章

  1. HashMap HashTable和ConcurrentHashMap的区别

    HashMap和Hashtable都实现了Map接口,其主要的区别有:线程安全性,同步(synchronization),以及效率. HashMap和Hashtable基本上没啥区别,除了HashMa ...

  2. 集合 HashMap 的原理,与 Hashtable、ConcurrentHashMap 的区别

    一.HashMap 的原理 1.HashMap简介 简单来讲,HashMap底层是由数组+链表的形式实现,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表 ...

  3. Java Main Differences between HashMap HashTable and ConcurrentHashMap

    转自这篇帖子:http://www.importnew.com/7010.html HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以 ...

  4. HashMap和Hashtable以及ConcurrentHashMap的区别

    ​ HashMap和Hashtable的区别 何为HashMap HashMap是在JDK1.2中引入的Map的实现类. HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部 ...

  5. HashMap、HashTable、ConcurrentHashMap的区别

    一.相关概念 1.Map的概念 javadoc中对Map的解释如下: An objectthat maps keys to values . Amap cannot contain duplicate ...

  6. HashMap、Hashtable和ConcurrentHashMap的区别

    HashTable 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相 ...

  7. HashMap,HashTable ,LinkedHashMap,TreeMap的区别

    Map:主要是存储键值对,不允许键重复,但可以值重复. HashMap:根据键的HashCode值来存储数据,根据键直接获取值.具有很快的访问速度,遍历时,取得的数据值的顺序都是随机的.hashMap ...

  8. HashTable与ConcurrentHashMap的区别

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

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

随机推荐

  1. reduce要素与适用总结

    要素: 1.高阶函数:reduce: 2.处理函数:reducer: 3.数据:可以是具体数据.签名相同的普通函数.签名相同的高阶函数: reduce(reducer, datas(data or f ...

  2. 文件夹上传组件webupload插件

    javaweb上传文件 上传文件的jsp中的部分 上传文件同样可以使用form表单向后端发请求,也可以使用 ajax向后端发请求 1.通过form表单向后端发送请求 <form id=" ...

  3. MySQL中SUM和COUNT的区别

    COUNT:是对记录进行汇总,即计数 SUM:是对符合条件的数值列字段进行求和 原表数据如下: 1,当在where子句中使用Price>25时, COUNT函数返回的是符合条件的记录,SUM函数 ...

  4. JS的ES6的Generator

    JS的ES6的Generator 1.Generator函数的概念: ES6提供的解决异步编程的方案之一,现在已经不怎么用了被淘汰了. Generator函数是一个状态机,内部封装了不同状态的数据. ...

  5. 为什么vue组件中的data不是一个对象而是一个函数

    如果两个实例引用同一个对象,当其中一个实例的属性发生改变时,另一个实例属性也随之改变,只有当两个实例拥有自己的作用域时,才不会相互干扰. 这是因为JavaScript的特性所导致,在component ...

  6. gym102222 G. Factories

    gym102222 G. Factories 地址 题目大意: 给一棵n个点的树,选m个点,这m个点只能在叶子节点上,问着m个点中两两之间到达其余各点的距离和最小值是多少题解:任意两点的树上距离和问题 ...

  7. <每日 1 OJ> -LeetCode 7. 整数反转

    题目描述 给定一个 32 位有符号整数,将整数中的数字进行反转. 示例 1: 输入: 123 输出: 321  示例 2: 输入: -123 输出: -321 示例 3: 输入: 120 输出: 21 ...

  8. 【Beta】发布说明

    再次号外!Visual Pytorch第二个版本上线了! 目前的网址在这里(http://114.115.151.39/) 哦 有关上一版本的功能介绍说明请参考博客:Visual Pytorch -- ...

  9. 用Python画一颗特别的心送给她

    import numpy as np import matplotlib.pyplot as plt x_coords = np.linspace(-100, 100, 500) y_coords = ...

  10. js Map的使用

    setExpenseAndAmountSum: function() { var detailList = vehicleVueObj.vehicleData; var expenseAmountSu ...