Java 如何生成高效的哈希代码?
我有三种hashCode方法,如下所示,我根据它们的效率对它们进行了优先级排序。我想知道是否有其他方法可以使hashCode方法更有效Java 如何生成高效的哈希代码?,java,performance,hashcode,Java,Performance,Hashcode,我有三种hashCode方法,如下所示,我根据它们的效率对它们进行了优先级排序。我想知道是否有其他方法可以使hashCode方法更有效 1) public int hashCode() { //terrible return 5; } 2) public int hashCode() { //a bit less terrible return name.length; } 3) public int hashCode() { //better fi
1) public int hashCode() { //terrible
return 5;
}
2) public int hashCode() { //a bit less terrible
return name.length;
}
3) public int hashCode() { //better
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
我根据他们的效率对他们进行了优先排序
如果您所说的“效率”是指应用程序的性能,而不是与其他方法隔离的hashCode
方法的延迟,那么您的列表是按升序效率排序的。分散性差的哈希代码将导致通过HashMap
中的链接列表进行线性或近似线性搜索,从而完全取消哈希表的优势
特别要注意的是,在今天的体系结构上,计算比指针解引用便宜得多,而且成本固定。一次缓存未命中相当于一千次简单的算术运算,每个指针取消引用都是一次潜在的缓存未命中。没有可靠的方法保证您的
hashcode
函数是最优的,因为它是由两个不同的度量来衡量的
- 效率计算速度有多快
- 碰撞-碰撞的可能性有多大
枚举
的序数
),这一点是显而易见的
有时,记住这些值是一个很好的解决方案——通过这种方式,即使是非常低效的方法也可以得到缓解,因为它只计算一次。这有一个明显的理论成本,也必须加以平衡
有时,代码的整体功能会影响您的选择。假设要将文件
对象放入哈希映射
。有许多选择是明确的:
hashcode
的主要用途之一是将对象插入到HashMap
中。该算法从对象请求一个哈希代码,并使用该哈希代码决定将对象放入哪个bucket。如果散列与另一个对象发生冲突,则该存储桶中将有另一个对象,在这种情况下,存储桶将必须增长,这需要花费时间。如果所有散列都是唯一的,那么映射将是每个bucket一个项,因此效率最高
有关
HashMap
如何工作的深入讨论,请参阅上的优秀WikiPedia文章。我的答案走了一条不同的道路-基本上,这不是答案,而是一个问题:为什么要担心hashCode()
的性能
您是否对您的应用程序进行了详尽的分析,并发现在某些对象上存在源于该方法的性能问题
如果这个问题的答案是“不”。。。那么-为什么你认为你需要担心这个方法呢?为什么您认为eclipse生成的默认值可能每天使用数十亿次。。。对你来说还不够好吗
请参阅,了解为什么在这些问题上浪费时间通常是一个非常糟糕的主意 是的,有更好的选择
或者是速度更快、质量更好的通用哈希算法。除了迄今为止有价值的答案之外,我还想添加一些其他方法来考虑:
3a): 在性能方面没有太多的优点/缺点,但更简洁一些
4.)您应该提供更多关于您正在谈论的类的信息,或者重新考虑您的设计。但是,当类的唯一属性是
字符串时,使用类作为哈希映射的键,那么您也可以直接使用该字符串。因此,选择4是:
// Changing this...
Map<Key, Value> map;
map.put(key, value);
Value value = map.get(key);
// ... to this:
Map<String, Value> map;
map.put(key.getName(), value);
Value value = map.get(key.getName());
当然,这只对不可变类有意义。您应该知道,使用可变类作为Map
的键是“危险的”,可能会导致一致性错误,并且只有在绝对确定用作键的实例不会更改时,才应该这样做
因此,如果您想使用类作为键,并且您的类甚至可能不止一个字段,那么您可以将哈希代码存储为字段:
class Key
{
private final String name;
... // Other fields...
private final int hashCode;
Key(String name, ...)
{
this.name = name;
... // Other fields
// Pre-compute and store the hash code:
this.hashCode = computeHashCode();
}
private int computeHashCode()
{
int result = 31;
result = 31 * result + Objects.hashCode(name);
result = 31 * result + ... // Other fields
return result;
}
}
第一个刚回来…5。。那威尼斯有什么好处?旁注:name.length
一点也不好,也没那么可怕:为什么不自动生成它?这里有一个新颖的想法。。。返回name.hashCode()怎么样代码>。唯一影响hashcode的似乎是name
的值。如果它是一个字符串
,它将返回一个好的hashcode。编辑:显然它不是一个字符串,而是一个数组。您可以返回它的hashcode,也可以使用Arrays.hashcode()
生成它。效率是一个主观的衡量标准,在很大程度上取决于上下文。是否需要在散列数据结构中快速插入和/或查找?输入数据的分布特征是什么?大多数情况下,在遇到性能问题并查明实际瓶颈之前,不要尝试进行优化。感谢您的回答,请详细说明指针解引用?另外,我有点搞不清哪一个更好?在Java级别上,只要访问一个对象(读取对它的引用,然后从它指向的内存位置读取),指针解引用就会发生。最好的哈希代码是3),因为它将导致哈希表中条目的最佳分布,使它们可以在O(1)时间而不是O(n)时间内访问
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
...
/** Cache the hash code for the string */
private int hash; // Default to 0
class Key
{
private final String name;
... // Other fields...
private final int hashCode;
Key(String name, ...)
{
this.name = name;
... // Other fields
// Pre-compute and store the hash code:
this.hashCode = computeHashCode();
}
private int computeHashCode()
{
int result = 31;
result = 31 * result + Objects.hashCode(name);
result = 31 * result + ... // Other fields
return result;
}
}