Java中HashMap和Hashtable之间的区别是什么?
对于非线程应用程序,哪个更有效?
Java中HashMap和Hashtable之间的区别是什么?
对于非线程应用程序,哪个更有效?
Hashtable是同步的,而HashMap不是。这使得Hashtable比Hashmap慢。
对于单线程应用程序,请使用HashMap,因为它们在功能方面是相同的。
Hashtable与HashMap类似,具有类似的接口。建议您使用HashMap,除非您需要支持遗留应用程序或需要同步,因为Hashtable方法是同步的。因此,在您不是多线程的情况下,HashMaps是您的最佳选择。
除了izb所说的,HashMap允许空值,而Hashtable不允许。
还要注意,Hashtable扩展了Dictionary类,作为Javadocs状态,该类已过时,已被Map接口取代。
Java中HashMap和Hashtable之间有几个不同之处:
Hashtable是同步的,而HashMap不是。这使得HashMap更适合非线程应用程序,因为非同步对象通常比同步对象执行得更好。哈希表不允许空键或值。HashMap允许一个空键和任意数量的空值。HashMap的子类之一是LinkedHashMap,因此,如果您需要可预测的迭代顺序(默认情况下是插入顺序),您可以很容易地将HashMap替换为LinkedHashMap。如果您使用Hashtable,这将不会那么容易。
由于同步对您来说不是问题,所以我推荐HashMap。如果同步成为问题,您还可以查看ConcurrentHashMap。
HashMap:使用哈希代码对数组进行索引的Map接口的实现。哈希表:嗨,1998年来电话。他们想拿回他们的集合API。
说真的,你最好完全远离Hashtable。对于单线程应用程序,您不需要额外的同步开销。对于高度并发的应用程序,偏执的同步可能会导致饥饿、死锁或不必要的垃圾收集暂停。正如Tim Howland所指出的,您可以使用ConcurrentHashMap。
注意,很多答案都表明Hashtable是同步的。在实践中,这几乎不会给你带来什么好处。同步是在访问器/赋值器上进行的。方法将停止同时从映射中添加或删除两个线程,但在现实世界中,您通常需要额外的同步。
一个非常常见的习惯用法是“先检查后放”,即在Map中查找条目,如果它不存在,则添加它。无论使用Hashtable还是HashMap,这都不是原子操作。
可通过以下方式获得等效同步的HashMap:
Collections.synchronizedMap(myMap);
但要正确实现此逻辑,您需要以下形式的额外同步:
synchronized(myMap) {
if (!myMap.containsKey("tomato"))
myMap.put("tomato", "red");
}
即使迭代Hashtable的条目(或Collections.synchronizedMap获得的HashMap)也不是线程安全的,除非您还通过额外的同步来防止Map被修改。
ConcurrentMap接口(例如ConcurrentHashMap)的实现通过包括线程安全检查然后动作语义(如:
ConcurrentMap.putIfAbsent(key, value);
哈希表被认为是遗留代码。关于Hashtable,没有什么不能使用HashMap或HashMap的派生来完成的,所以对于新代码,我看不出任何理由返回Hashtable。
hashtable和hashmap之间的另一个关键区别是,hashmap中的Iterator是快速失败的,而hashtable的枚举器不是快速失败的。如果任何其他线程通过添加或删除Iterator自己的remove()方法以外的任何元素来从结构上修改映射,则抛出ConcurrentModificationException。但这不是一种保证的行为,将由JVM尽最大努力完成。"
我的来源:http://javarevisited.blogspot.com/2010/10/difference-between-hashmap-and.html
面试中经常会问这个问题,以检查应聘者是否了解集合类的正确用法,并了解可用的替代解决方案。
HashMap类大致等同于Hashtable,只是它是非同步的并且允许为null。(HashMap允许空值作为键和值,而Hashtable不允许空值)。HashMap不能保证映射的顺序随时间保持不变。HashMap是非同步的,而Hashtable是同步的。HashMap中的迭代器是故障安全的,而Hashtable的枚举器不是,如果任何其他线程通过添加或删除迭代器自己的remove()方法以外的任何元素来从结构上修改映射,则引发ConcurrentModificationException。但这不是一种保证的行为,将由JVM尽最大努力完成。
关于一些重要条款的说明:
同步意味着只有一个线程可以在一个时间点修改哈希表。基本上,这意味着在对Hashtable执行更新之前,任何线程都必须获取对象的锁,而其他线程将等待释放锁。故障安全在迭代器的上下文中是相关的。如果在集合对象上创建了迭代器,并且其他线程试图“从结构上”修改集合对象,则将引发并发修改异常。但其他线程也可以调用set方法,因为它不会“从结构上”修改集合。但是,如果在调用set之前,集合已经在结构上进行了修改,则将抛出IllegalArgumentException。结构修改意味着删除或插入可以有效改变地图结构的元素。
HashMap可以通过
Map m=Collections.synchronizedMap(hashMap);
Map提供集合视图,而不是通过枚举对象直接支持迭代。集合视图极大地增强了界面的表现力,这将在本节稍后讨论。Map允许您遍历键、值或键值对;哈希表不提供第三个选项。Map提供了一种在迭代过程中删除条目的安全方法;哈希表没有。最后,Map修复了Hashtable接口中的一个小缺陷。Hashtable有一个名为contains的方法,如果Hashtable包含给定值,则返回true。给定它的名称,如果Hashtable包含给定的键,您会希望该方法返回true,因为该键是Hashtable的主要访问机制。Map接口通过重命名方法containsValue消除了这种混淆。此外,这提高了接口的一致性-containsValue与containsKey类似。
地图界面
除了这里已经提到的所有其他重要方面之外,CollectionsAPI(例如Map接口)一直在进行修改,以符合Java规范的“最新和最大”添加。
例如,比较Java 5 Map迭代:
for (Elem elem : map.keys()) {
elem.doSth();
}
与旧的Hashtable方法相比:
for (Enumeration en = htable.keys(); en.hasMoreElements(); ) {
Elem elem = (Elem) en.nextElement();
elem.doSth();
}
在Java 1.8中,我们还承诺能够像使用好的旧脚本语言一样构造和访问HashMaps:
Map<String,Integer> map = { "orange" : 12, "apples" : 15 };
map["apples"];
更新:不,他们不会在1.8…:(
Project Coin的收藏增强功能是否将在JDK8中?
HashTable是同步的,如果您在单个线程中使用它,则可以使用HashMap,这是一个未同步的版本。未同步的对象通常性能更高。顺便说一句,如果多个线程同时访问HashMap,并且至少有一个线程在结构上修改了该映射,那么它必须在外部同步。您可以使用以下方法将未同步的映射打包到已同步的映射中:Map m=Collections.synchronizedMap(新HashMap(…));HashTable只能包含非空对象作为键或值。HashMap可以包含一个空键和空值。Map返回的迭代器是快速失败的,如果在创建迭代器之后的任何时候对映射进行了结构修改,则除了通过迭代器自己的remove方法之外,迭代器将抛出ConcurrentModificationException。因此,在并发修改的情况下,迭代器会快速而干净地失败,而不是在未来某个不确定的时间冒着任意、非确定性行为的风险。而Hashtable的键和元素方法返回的枚举不会快速失败。HashTable和HashMap是Java集合框架的成员(自从Java 2平台v1.2以来,HashTable被改进以实现Map接口)。HashTable被认为是遗留代码,如果需要线程安全的高度并发实现,文档建议使用ConcurrentHashMap代替HashTable。HashMap不保证返回元素的顺序。对于HashTable,我想它是相同的,但我不完全确定,我找不到明确说明这一点的ressource。
HashMap和Hashtable也有显著的算法差异。以前没有人提到过这一点,所以我才提出来。HashMap将构造一个大小为两倍的哈希表,动态地增加它,以便在任何bucket中最多有八个元素(冲突),并且对于一般的元素类型,可以很好地搅拌这些元素。然而,如果你知道你在做什么,Hashtable实现提供了对哈希的更好和更精细的控制,也就是说,你可以使用最接近你的值域大小的素数来固定表大小,这将导致比HashMap更好的性能,即在某些情况下更少的冲突。
除了在这个问题中广泛讨论的明显差异之外,我认为Hashtable是一款“手动驾驶”汽车,您可以更好地控制哈希,而HashMap是“自动驾驶”的对应车型,通常表现良好。
Hashtable是同步的,而HashMap不是。另一个区别是HashMap中的迭代器是故障安全的而Hashtable的枚举器不是。如果您更改地图在迭代时,你会知道。HashMap允许其中包含空值,而Hashtable不允许。
HashTable是jdk中的一个遗留类,不应再使用。将其用法替换为ConcurrentHashMap。如果您不需要线程安全,请使用HashMap,它不是线程安全的,但速度更快,占用的内存更少。
HashMap是一个用于以键和值格式存储元素的类。它不是线程安全的。因为它不同步。其中as Hashtable是同步的。Hashmap允许null,但hastable不允许null。
HashTable和HashMaps有5个基本区别。
映射允许您迭代和检索键、值以及两个键-值对,而HashTable不具备所有这些功能。Hashtable中有一个函数contains(),使用起来非常混乱。因为contains的意思稍有偏差。它是指包含键还是包含值?很难理解。在Maps中,我们有ContainsKey()和ContainsValue()函数,这两个函数非常容易理解。在hashmap中,您可以在迭代时安全地删除元素。因为这在哈希表中是不可能的。HashTables默认情况下是同步的,因此可以轻松地与多个线程一起使用。默认情况下,as HashMaps不同步,因此只能用于单个线程。但您仍然可以使用Collections-util类的synchronizedMap(Mapm)函数将HashMap转换为synchronized。HashTable不允许空键或空值。其中作为HashMap允许一个空键和多个空值。
由于Java中的Hashtable是Dictionary类的一个子类,现在由于Map接口的存在而过时,所以不再使用它。此外,对于一个实现Map接口的类,没有什么是不能用Hashtable做的。
我的小贡献:
Hashtable和HashMap之间的第一个也是最重要的区别是,HashMap不是线程安全的,而Hashtable是线程安全的集合。Hashtable和HashMap之间的第二个重要区别是性能,因为HashMap不同步,所以性能比Hashtable好。Hashtable与HashMap的第三个区别是Hashtable是过时的类,应该在Java中使用ConcurrentHashMap代替Hashtable。
请记住,HashTable是在引入Java集合框架(JCF)之前的遗留类,后来经过了修改以实现Map接口。Vector和Stack也是如此。
因此,在新代码中始终远离它们,因为正如其他人所指出的,JCF中总是有更好的替代方案。
这是Java集合备忘单,您会发现它很有用。请注意,灰色块包含旧类HashTable、Vector和Stack。
HashMap和HashTable
关于HashMap和HashTable的一些要点。请阅读以下详细信息。
1) Hashtable和Hashmap实现java.util.Map接口2) Hashmap和Hashtable都是基于哈希的集合。并致力于哈希。所以这些是HashMap和HashTable的相似性。
HashMap和HashTable之间的区别是什么?
1) 第一个区别是HashMap不是线程安全的,而HashTable是ThreadSafe的2) HashMap性能更好,因为它不是线程安全的。而Hashtable的性能并不是更好,因为它是线程安全的。因此多个线程不能同时访问Hashtable。
古老而经典的主题,只想添加一个有用的博客来解释这一点:
http://blog.manishchhabra.com/2012/08/the-5-main-differences-betwen-hashmap-and-hashtable/
博客作者:Manish Chhabra
HashMap和Hashtable之间的5个主要区别HashMap和Hashtable都实现java.util.Map接口Java开发人员编写时必须了解一些差异更高效的代码。从Java 2平台v1.2开始,Hashtable类被改装以实现Map接口,使其成为Java集合框架。HashMap和Hashtable之间的主要区别之一是HashMap是非同步的,而Hashtable是同步的表示Hashtable是线程安全的,可以在多个线程,但HashMap不能在多个线程之间共享正确的同步。Java 5引入了ConcurrentHashMapHashtable的替代方案,提供了比Hashtable更好的可扩展性Java中的哈希表。同步意味着只有一个线程可以修改哈希在某个时间点。基本上,这意味着之前的任何线程对哈希表执行更新必须获取对象,而其他人将等待释放锁定。HashMap类大致等同于Hashtable,只是它允许空值。(HashMap允许空值作为键和值,而哈希表不允许空值)。HashMap与Hashtable之间的第三个显著区别是HashMap中的迭代器是一个快速失败迭代器,而Hashtable的枚举器不是和抛出如果任何其他线程修改映射,则并发修改异常通过添加或删除除迭代器自身元素之外的任何元素,在结构上remove()方法。但这不是一种保证行为由JVM尽最大努力完成。这也是一个重要的区别在Java中枚举和迭代器之间。Hashtable和HashMap之间的一个更显著的区别是,由于线程安全和同步,Hashtable要慢得多如果在单线程环境中使用,则比HashMap更好。所以如果你不需要同步,HashMap仅由一个线程使用在Java中执行Hashtable。HashMap不能保证映射的顺序随时间保持不变。注意HashMap可以通过Map m=Collections.synchronizedMap(hashMap);总之,Hashtable和Java中的HashMap,例如线程安全性和速度,仅基于此如果您绝对需要线程安全,请使用HashtableJava5考虑在Java中使用ConcurrentHashMap。
1.Hashmap和HashTable都存储键和值。
2.Hashmap可以将一个键存储为null。哈希表不能存储null。
3.HashMap未同步,但Hashtable已同步。
4.HashMap可以与Collection.SyncronizedMap(map)同步
Map hashmap = new HashMap();
Map map = Collections.SyncronizedMap(hashmap);
哈希表:
Hashtable是一种保留键值对值的数据结构。它不允许键和值都为null。如果添加null值,将获得NullPointerException。它是同步的。因此,它有其成本。在特定时间,只有一个线程可以访问HashTable。
例子:
import java.util.Map;
import java.util.Hashtable;
public class TestClass {
public static void main(String args[ ]) {
Map<Integer,String> states= new Hashtable<Integer,String>();
states.put(1, "INDIA");
states.put(2, "USA");
states.put(3, null); //will throw NullPointerEcxeption at runtime
System.out.println(states.get(1));
System.out.println(states.get(2));
// System.out.println(states.get(3));
}
}
哈希映射:
HashMap类似于Hashtable,但它也接受键值对。它允许键和值都为空。它的性能优于HashTable,因为它是非同步的。
例子:
import java.util.HashMap;
import java.util.Map;
public class TestClass {
public static void main(String args[ ]) {
Map<Integer,String> states = new HashMap<Integer,String>();
states.put(1, "INDIA");
states.put(2, "USA");
states.put(3, null); // Okay
states.put(null,"UK");
System.out.println(states.get(1));
System.out.println(states.get(2));
System.out.println(states.get(3));
}
}
同步或线程安全:
哈希映射不同步,因此不安全,如果没有适当的同步块,则无法在多个线程之间共享,而哈希表是同步的,因此是线程安全的。
空键和空值:
HashMap允许一个空键和任意数量的空值。Hashtable不允许空键或值。
迭代值:
HashMap中的迭代器是一个快速失败迭代器,而Hashtable的枚举器不是,如果任何其他线程通过添加或删除除迭代器自己的remove()方法之外的任何元素来修改映射结构,则抛出ConcurrentModificationException。
超级类和传统:
HashMap是AbstractMap类的子类,而Hashtable是Dictionary类的子级。
性能:
由于HashMap不同步,因此与Hashtable相比,它更快。
参考http://modernpathshala.com/Article/1020/difference-between-hashmap-and-hashtable-in-java有关Java集合的示例、面试问题和测验
除了已经提到的差异之外,应该注意的是,自从Java8以来,HashMap动态地用TreeNodes(红黑树)替换每个bucket中使用的Nodes(链表),因此即使存在高哈希冲突,搜索时最坏的情况也是
HashMap的O(log(n))与Hashtable中的O(n)。
*上述改进尚未应用于Hashtable,而仅应用于HashMap、LinkedHashMap和ConcurrentHashMap。
仅供参考,目前,
TREEIFY_THRESHOLD=8:如果存储桶包含8个以上的节点,则链接列表将转换为平衡树。UNTREEIFY_THRESHOLD=6:当存储桶太小(由于删除或调整大小)时,树将转换回链接列表。
已经发布了许多好答案。我补充了一些新的观点并总结了一下。
HashMap和Hashtable都用于以键和值的形式存储数据。它们都使用哈希技术来存储唯一的键。但是HashMap和Hashtable类之间有很多不同,如下所示。
哈希图
HashMap未同步。它不是线程安全的,如果没有适当的同步代码,就无法在多个线程之间共享。HashMap允许一个空键和多个空值。HashMap是JDK1.2中引入的一个新类。HashMap速度很快。我们可以通过调用以下代码使HashMap同步Map m=Collections.synchronizedMap(HashMap);Iterator遍历HashMap。HashMap中的迭代器快速失败。HashMap继承AbstractMap类。
哈希表
哈希表已同步。它是线程安全的,可以与多个线程共享。哈希表不允许空键或值。Hashtable是一个遗留类。哈希表很慢。哈希表是内部同步的,不能不同步。枚举器和迭代器遍历哈希表。Hashtable中的枚举器不会快速失败。Hashtable继承Dictionary类。
进一步阅读Java中HashMap和Hashtable之间的区别是什么?
Hashtable类是同步的,也就是说,它被设计用于处理多线程或多线程进程的应用程序。在应用程序到进程的经典情况下,同步类的效率较低,因此Hashmap类通常更快。HashTable类不接受Null值(对于键或值),而HashMap类允许单个键为Null,并尽可能多地为Null。
集合(有时称为容器)只是将多个元素分组为单个单元的对象。集合用于存储、检索、操作和传递聚合数据。集合框架W是用于表示和操作集合的统一架构。
HashMap JDK1.2和Hashtable JDK1.0都用于表示一组对象,这些对象在<Key,Value>对中表示。每个<Key,Value>对都称为Entry对象。条目集合由HashMap和Hashtable的对象引用。集合中的键必须是唯一的或与众不同的。[因为它们用于检索特定键的映射值。集合中的值可以重复。]
«超级类、传统和收藏框架成员
Hashtable是JDK1.0中引入的一个遗留类,它是Dictionary类的一个子类。从JDK1.2哈希表被重新设计以实现Map接口,从而成为集合框架的成员。HashMap从JDK1.2引入之初就是Java集合框架的成员。HashMap是AbstractMap类的子类。
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable { ... }
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { ... }
«初始容量和负载系数
容量是哈希表中桶的数量,初始容量只是创建哈希表时的容量。注意,哈希表是开放的:在“哈希冲突”的情况下,一个存储桶存储多个条目,这些条目必须按顺序搜索。负载因子是在哈希表的容量自动增加之前,允许哈希表达到多满的度量。
HashMap使用默认初始容量(16)和默认负载因子(0.75)构造一个空哈希表。Where as Hashtable使用默认初始能力(11)和负载因子/填充比(0.75)构建空哈希表。
«哈希冲突时的结构修改
HashMap,Hashtable在哈希冲突的情况下,它们将映射条目存储在链接列表中。从Java8 for HashMap中,如果哈希桶增长超过某个阈值,该桶将从条目链接列表切换到平衡树。这将最坏情况性能从O(n)提高到O(logn)。在将列表转换为二进制树时,哈希代码用作分支变量。如果同一个存储桶中有两个不同的哈希码,一个被认为更大,并位于树的右侧,另一个位于左侧。但是,当两个哈希码相等时,HashMap假设键是可比较的,并比较键以确定方向,以便保持某种顺序。使HashMap的键具有可比性是一个很好的做法。在添加条目时,如果存储桶大小达到TREEIFY_THRESHOLD=8,则将条目的链接列表转换为平衡树;在删除小于TREEIFY_THRESHOLD且最多为UNTREEIFY_THRESHOLD=6的条目时,将重新将平衡树转换为条目的链接表。Java 8 SRC,堆栈
«集合视图迭代、快速故障和安全故障
+--------------------+-----------+-------------+
| | Iterator | Enumeration |
+--------------------+-----------+-------------+
| Hashtable | fail-fast | safe |
+--------------------+-----------+-------------+
| HashMap | fail-fast | fail-fast |
+--------------------+-----------+-------------+
| ConcurrentHashMap | safe | safe |
+--------------------+-----------+-------------+
迭代器本质上是一种快速故障。即,如果在迭代而不是自己的remove()方法时修改了集合,则会抛出ConcurrentModificationException。其中作为枚举本质上是故障安全的。如果在迭代时修改了集合,则不会引发任何异常。
根据JavaAPI文档,迭代器总是优先于枚举。
注意:枚举接口的功能与迭代器接口重复。此外,Iterator添加了一个可选的移除操作,并且方法名称更短。新的实现应该考虑使用迭代器而不是枚举。
在Java5中引入了ConcurrentMap接口:ConcurrentHashMap——一个由哈希表支持的高度并发、高性能的ConcurrentMap实现。此实现在执行检索时从不阻塞,并允许客户端选择更新的并发级别。它旨在作为Hashtable的替代品:除了实现ConcurrentMap之外,它还支持Hashtable特有的所有“遗留”方法。
每个HashMapEntrys值都是可变的,从而确保争用修改和后续读取的细粒度一致性;每次读取都反映最近完成的更新迭代器和枚举是故障安全的-反映自创建迭代器/枚举以来的某个点的状态;这允许以降低一致性为代价同时进行读取和修改。它们不会引发ConcurrentModificationException。然而,迭代器被设计为一次只能由一个线程使用。与Hashtable类似,但与HashMap不同,此类不允许将null用作键或值。
public static void main(String[] args) {
//HashMap<String, Integer> hash = new HashMap<String, Integer>();
Hashtable<String, Integer> hash = new Hashtable<String, Integer>();
//ConcurrentHashMap<String, Integer> hash = new ConcurrentHashMap<>();
new Thread() {
@Override public void run() {
try {
for (int i = 10; i < 20; i++) {
sleepThread(1);
System.out.println("T1 :- Key"+i);
hash.put("Key"+i, i);
}
System.out.println( System.identityHashCode( hash ) );
} catch ( Exception e ) {
e.printStackTrace();
}
}
}.start();
new Thread() {
@Override public void run() {
try {
sleepThread(5);
// ConcurrentHashMap traverse using Iterator, Enumeration is Fail-Safe.
// Hashtable traverse using Enumeration is Fail-Safe, Iterator is Fail-Fast.
for (Enumeration<String> e = hash.keys(); e.hasMoreElements(); ) {
sleepThread(1);
System.out.println("T2 : "+ e.nextElement());
}
// HashMap traverse using Iterator, Enumeration is Fail-Fast.
/*
for (Iterator< Entry<String, Integer> > it = hash.entrySet().iterator(); it.hasNext(); ) {
sleepThread(1);
System.out.println("T2 : "+ it.next());
// ConcurrentModificationException at java.util.Hashtable$Enumerator.next
}
*/
/*
Set< Entry<String, Integer> > entrySet = hash.entrySet();
Iterator< Entry<String, Integer> > it = entrySet.iterator();
Enumeration<Entry<String, Integer>> entryEnumeration = Collections.enumeration( entrySet );
while( entryEnumeration.hasMoreElements() ) {
sleepThread(1);
Entry<String, Integer> nextElement = entryEnumeration.nextElement();
System.out.println("T2 : "+ nextElement.getKey() +" : "+ nextElement.getValue() );
//java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextNode
// at java.util.HashMap$EntryIterator.next
// at java.util.Collections$3.nextElement
}
*/
} catch ( Exception e ) {
e.printStackTrace();
}
}
}.start();
Map<String, String> unmodifiableMap = Collections.unmodifiableMap( map );
try {
unmodifiableMap.put("key4", "unmodifiableMap");
} catch (java.lang.UnsupportedOperationException e) {
System.err.println("UnsupportedOperationException : "+ e.getMessage() );
}
}
static void sleepThread( int sec ) {
try {
Thread.sleep( 1000 * sec );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
«空键和空值
HashMap最多允许一个空键和任意数量的空值。如果as Hashtable甚至不允许单个null键和null值,那么如果键或值为null,则会引发NullPointerException。实例
«同步,线程安全
哈希表是内部同步的。因此,在多线程应用程序中使用Hashtable非常安全。其中as HashMap未在内部同步。因此,在没有外部同步的多线程应用程序中使用HashMap是不安全的。可以使用Collections.synchronizedMap()方法从外部同步HashMap。
«性能
由于Hashtable是内部同步的,这使得Hashtable比HashMap稍慢。
@See
红黑树是一种自平衡的二叉搜索树Java8中HashMap的性能改进
HashMap和Hashtable都用于以键和值的形式存储数据。它们都使用哈希技术来存储唯一的键。但是HashMap和Hashtable类之间有很多不同,如下所示。