最佳查找结构是HashTable
。它提供了平均的持续访问(在最坏的情况下是线性的)
这取决于散列函数。好吧。
我的问题如下。假设HashTable
的良好实施例如HashMap
是否有关于地图中传递的密钥的最佳实践?我的意思是建议密钥必须是不可变对象,但我想知道是否还有其他建议。
示例键的大小?例如,如果我们使用String
作为键,那么在一个好的hashmap中(以上述方式),equals
(试图找到密钥)的字符串比较中不会出现“瓶颈”吗?钥匙要保持小吗?或者是否有不应该用作键的对象?例如。一个URL
?在这种情况下,您如何选择使用什么作为关键?
答案 0 :(得分:1)
您应该使用您想要使用的任何键来查找数据结构中的内容,它通常是特定于域的约束。有了这个,请记住,hashCode()
和equals()
都将用于查找表中的键。
hashCode()
用于查找密钥的位置,而equals()
用于确定您要搜索的密钥是否实际上是我们刚使用hashCode()
找到的密钥。
例如,考虑使用separate chaining在表中具有相同哈希码的两个键a
和b
。然后,一旦我们找到包含a
的列表的索引,则搜索a.equals(key)
将需要对表中的a
和b
进行a
测试来自b
的{{1}}。
答案 1 :(得分:1)
HashMap表现最佳的密钥可能是整数,其中hashCode()
和equals()
实现为:
public int hashCode() {
return value;
}
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
说,HashMap的目的是将一些对象(值)映射到其他对象(键)。散列函数用于寻址(值)对象的事实是提供快速,恒定时间的访问。
建议密钥必须是不可变对象,但我想知道是否还有其他建议。
建议将对象映射到你需要的东西:不要认为什么是更快的;但是想想你的业务逻辑最适合解决要检索的对象。
重要的要求是密钥对象必须是不可变的,因为如果在将密钥对象存储到Map中之后更改密钥对象,则以后可能无法检索关联的值。
HashMap
中的关键字为Map
。您的对象应该只是 map 。如果你牺牲了优化密钥的映射任务,那么你就无法实现Map的目的 - 而不会实现任何性能提升。
我100%同意你问题中的前两条评论:
主要的限制是它必须是你想要查找的东西;)
- 奥利查尔斯沃思一般规则是使用任何你需要查看的关键词 - Louis Wasserman
请记住优化的两条规则:
第三条规则是:优化前的个人资料。
答案 2 :(得分:0)
我挖掘了实施。我假设hashCode()
方法的有效性将是关键因素。
当我查看HashMap()
和Hashtable()
实现时,我发现实现非常相似(有一个例外)。两者都在为所有条目使用和存储内部哈希码,因此hashCode()
不会对性能产生如此大的影响。
两者都有许多存储桶,存储值。桶的数量(比如n)和桶内的平均密钥数(比如k)之间的重要平衡。桶在O(1)时间内找到,桶的内容以O(k)大小迭代,但是我们拥有的桶越多,分配的内存就越多。此外,如果许多存储桶为空,则意味着密钥类的hashCode()
方法的哈希码不够宽。
算法的工作原理如下:
Take the `hashCode()` of the Key (and make a slight bijective transformation on it)
Find the appropriate bucket
Loop through the content of the bucket (which is some kind of LinkedList)
Make the comparison of the keys as follows:
1. Compare the hashcodes
(it is calculated in the first step, and stored for the entry)
2. Examine if key `==` the stored key (still no call)
(this step is missing from Hashtable)
3. Compare the keys by `key.equals(storedKey)`
总结:
相同的算法适用于get()
和put()
(因为在put()情况下,您可以设置现有密钥的值)。因此,最重要的是如何实施hashCode()
方法。这是最常被称为的方法。
两种策略是:快速和使其有效(传播良好)。 JDK开发人员努力做到这两点,但并不总是可以同时使用它们。
Numeric
类型很好Object
(和非重写类)很好(hashCode()
是原生的),但您无法指定自己的equals()
String
不好,遍历字符,但之后缓存(请参阅下面的评论)对字符串的评论:为了使其快速,在JDK的第一个版本中,仅对前32个字符进行字符串哈希码计算。但它产生的哈希码并没有很好地传播,因此他们决定将所有字符都带入哈希码。
答案 3 :(得分:0)
建议密钥必须是不可变对象,但我想知道是否还有其他建议。
值的关键字应为final
。
大多数情况下,对象的字段用作键。如果该字段发生更改,则地图无法找到它:
void foo(Employee e) {
map.put(e.getId(), e);
String newId = e.getId() + "new";
e.setId(newId);
Employee e2 = e.get(newId);
// e != e2 !
}
所以Employee
根本不应该有setId()
方法,但这很难,因为当你写Employee
时,你不知道它将被键入什么。