论述题 17.  Hashtable和ConcurrentHashMap的区别是什么?
【正确答案】Hashtable是一种能提供快速插入和查询的数据结构,无论其包含有多少Item(条目),执行查询和插入操作的平均时间复杂度总是接近O(1)。
   ConcurrentHashMap是Java5中支持高并发、高吞吐量的线程安全HashMap实现。它由Segment数组结构和HashEntry数组结构组成。ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键一值对数据。一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构;一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素;每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。
   Hashtable和ConcurrentHashMap存储的内容为键一值对(key-value),且它们都是线程安全的容器,下面简要介绍它们的实现方式并对比它们的不同点。
   Hashtable所有的方法都是同步的,因此,它是线程安全的。它的定义如下:
   public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>,Cloneable,Serializable
   Hashtable是通过“拉链法”实现的散列表,因此,它使用数组+链表的方式来存储实际的元素,如图1所示。
   

   图1  HashTable结构

   在图1中,最顶部标数字的部分是一个Entry数组,而Entry又是一个链表。当向Hashtable中插入数据的时候,首先通过键的hashCode和Entry数组的长度来计算这个值应该存放在数组中的位置index。如果index对应的位置没有存放值,则直接存放到数组的index位置即可,当index有冲突的时候,则采用“拉链法”来解决冲突。假如往Hashtable中插入“aaa”“bbb”“eee”“fff”,如果“aaa”和“fff”所得到的index是相同的,则插入后Hashtable的结构如图2所示。
   

   图2  插入后Hashtable结构

   Hashtable的实现类图如图3所示。
   为了使Hashtable拥有比较好的性能,数组的大小也需要根据实际插入数据的多少来进行动态地调整。Hashtable类中定义了一个rehash方法,该方法可以用来动态地扩充Hashtable的容量,该方法被调用的时机为:Hashtable中的键一值对超过某一阂值。默认情况下,该阈值等于Hashtable中Entry数组的长度*0.75。Hashtable默认的大小为11,当达到阈值后,每次按照下面的公式对容量进行扩充:newCapacity=oldCapacity*2+1。
   

   图3  Hashtable实现类图

   Hashtable通过使用synchronized修饰方法的方式来实现多线程同步,因此,Hashtable的同步会锁住整个数组。在高并发的情况下,性能会非常差,Java5中引入java.util.concurrent.ConcurrentHashMap作为高吞吐量的线程安全HashMap实现,它采用了锁分离的技术允许多个修改操作并发进行。它们在多线程锁的使用方式如图4所示。
   
【答案解析】