HashMap中的字符串作为键

4

我曾看到,在HashMap中只有String被用作键。虽然put()方法接受Object类型的参数,但这有什么重要性呢?其他对象能否也被用作键呢?请提供答案。


1
@Michael 你怎么知道这是一个Java问题? - anon
5
你是否了解其他语言中有没有类似Java中的HashMap类,其中put()方法接受Object作为参数? - BalusC
6个回答

9
任何一个具有有意义实现的hashCode()方法的对象都是在Map中作为键的完美候选。详情请参见理解HashMap中equals和hashCode的工作原理

此外,正如@Jon所提到的,你的Map中的所有键应该是相同类型的。
编辑:当然,你需要同时实现equals()hashcode()方法。我认为其他问题链接的标题已经很清楚地表明了这一点。但一个愚蠢的hashcode()方法只会给你带来性能差的退化HashMap
编辑2:正如@Adrian在他的答案中提到的,泛型可以帮助你限制Map的键和值的类型。
参考资料:

当然,如果你重写了hashCode()方法,你也必须重写equals()方法,否则你的哈希映射可能会出现一些奇怪的问题。 - Ash
不行,只有实现了equals和hash(或者两者都没有实现)的对象才能用作键。 - akuhn
@Adrian > 这是暗示我正在链接到另一个答案...然而,使用一个简单的哈希码实现来同时实现equals和hashcode将毫无意义... - Gregory Pakosz

6

一个原始的 HashMap 确实可以接受任何对象作为键。然而,在映射中指定要使用哪种键和值是很好的风格。

Map<String, Whatever> map = new HashMap<String, Whatever>();

如果这样做,put()方法只会接受字符串。
注意:如果您选择使用自己的类作为键,请确保该类实现了equalshashCode中的两个方法或者两个方法都没有实现!

3

你经常看到"String"被用作哈希键的原因可能是它是一个不可变类。

不可变对象非常适合用作映射键,因为一旦它们在映射或集合中,就不必担心它们的值被修改,否则会破坏映射或集合的不变性(《Effective Java》第二版第15项)。


另外,字符串已经内置了equals()方法。 - Kevin Kostlan

2

您没有说明您所谈论的平台,但我暂时假设它是Java。

HashMap中,您可以使用任何类型作为键,但假设您使用完整的泛型版本,则需要指定该类型并始终使用它。例如,您可以使用URI作为键类型:

HashMap<URI, Integer> hitCountMap = new HashMap<URI, Integer>();

get方法的key参数类型为Object,但你应该使用和放入Map中相同类型的key。关于这个问题,可以参考这个问题进行更多讨论。


6
URL也许不是哈希表中关键字的最佳例子。从文档中可以看出,哈希码基于用于URL比较的所有URL组件。由于涉及与名称服务器解析URL,所以速度较慢,因此最好使用URI。当然,这完全是与这个问题无关的话题! - Jeff Foster

2

我们通常在HashMap中使用String作为键的原因之一是,由于Java中的String是不可变的,这使得String可以缓存其哈希码。由于String在Java中是不可变的,它会缓存其哈希码,而不是每次调用String的hashcode方法时都进行计算,这使得它作为HashMap键非常快速。


是的,但这只有在我们再次使用相同的String实例访问HashMap时才是真的,而不是因为hashCode(),而是因为equals()对于两个相同但不是同一实例的字符串需要O(字符串长度)的时间。 - kutschkem
在我的系统上(多年后),这允许将 ideavim 中的 Y 剪切行粘贴到 IDEA 之外的终端中,但不允许从终端复制的字符串通过 ideavim 的 p 进入我的代码。是否有什么变化? - alife

0

键类型可以是任何类型,包括(某些用例)Object。唯一的技术要求是对于所有可能用作键的类的实例,必须正确实现equals(Object)hashcode()。实际上,您还希望equals(Object)的语义在所有可能的键类型/值上与HashMap的预期行为一致。

然而,如果您确实需要使用Object作为键类型,则IdentityHashMap可能是更好的选择。首先,它不使用equals(Object)hashcode(),而是使用==和键对象的“标识哈希”值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接