点类的Java哈希代码
我有一个简单的自定义Point类,如下所示,我想知道我的hashCode实现是否可以改进,或者这是否是它将得到的最好的实现点类的Java哈希代码,java,hashcode,hash-code-uniqueness,Java,Hashcode,Hash Code Uniqueness,我有一个简单的自定义Point类,如下所示,我想知道我的hashCode实现是否可以改进,或者这是否是它将得到的最好的实现 public class Point { private final int x, y; public Point(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; }
public class Point
{
private final int x, y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
@Override
public boolean equals(Object other)
{
if (this == other)
return true;
if (!(other instanceof Point))
return false;
Point otherPoint = (Point) other;
return otherPoint.x == x && otherPoint.y == y;
}
@Override
public int hashCode()
{
return (Integer.toString(x) + "," + Integer.toString(y)).hashCode();
}
}
我曾经写过自己的hash和equals函数,后来我发现:) 当然,请记住以下几点: 因为反射涉及动态解析的类型, 无法执行某些Java虚拟机优化。 因此,反射操作的性能比其 非反射副本,并且应在代码部分避免使用 在性能敏感的应用程序中经常调用
我建议使用一种更简单、性能更高的不带字符串的方法,可能是Josh Bloch的方法,在您的例子中:
return 37 * x + y;
编辑:nybbler是正确的。实际上建议的是:
int result = 373; // Constant can vary, but should be prime
result = 37 * result + x;
result = 37 * result + y;
来自JDK的Point类(继承自Point2d):
这看起来比您的实现略好。您可以查看现有的点类型类实现:
/**
343 * Returns the hashcode for this <code>Point2D</code>.
344 * @return a hash code for this <code>Point2D</code>.
345 */
346 public int hashCode() {
347 long bits = java.lang.Double.doubleToLongBits(getX());
348 bits ^= java.lang.Double.doubleToLongBits(getY()) * 31;
349 return (((int) bits) ^ ((int) (bits >> 32)));
350 }
发件人:
可以找到hashCode实现的简单指南默认情况下,Eclipse将为您的Point类使用hashCode()函数,类似于:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + x;
result = prime * result + y;
return result;
}
至少,在hashCode算法中加入素数将有助于它的“唯一性”。请不要使用字符串。这背后有很多理论和一些实现(除法、乘法等)。如果你有一个小时的时间,你可以看这个 话虽如此,以下是Netbeans 7.1的建议:
@Override
public int hashCode() {
int hash = 7;
hash = 71 * hash + this.x;
hash = 71 * hash + this.y;
return hash;
}
2015年10月编辑
不久前我开始使用IntelliJ,现在我生活得更快乐了。这就是它的自动哈希代码生成所产生的结果。没那么冗长了。注意素数的使用
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
Gevorg建议的所有有效成员字段值的手动乘法可能是最有效的,并且具有良好的值分布。然而,如果您喜欢可读性,那么在Java7中也有不错的替代方案
import java.util.Objects;
...
@Override
public int hashCode() {
return Objects.hash(x, y);
}
。。。或在图书馆:
这两种varags方法都只是简单地委托给,因此由于int的自动装箱和创建对象引用数组,对性能的影响很小,但是它应该远没有使用反射那么重要
可读性非常好,因为您可以简单地看到,哪些字段用于hashcode计算,所有乘法和加法语法都隐藏在数组的遮罩下。hashcode(Object[]a)
:
将2D点散列为单个整数的一种非常好的方法是使用数字螺旋
@覆盖
公共int hashCode(){
int ax=数学绝对值(x);
int ay=数学绝对值(y);
如果(ax>ay&&x>0)返回4*x*x-3*x+y+1;
如果(ax>ay&&x sqrt(最大值)你打算如何改进它?你想让它更快吗?你想保证唯一性?速度?我想保证两个:)因为只有两个字段,你也可以使用这个库,但要明确列出字段。如果这个类在集合中被大量使用,反射哈希代码将是一个巨大的性能打击。我想d建议改为使用该方法。这与链接答案中的建议不完全一样。您没有注意到应该对每个字段单独执行此操作,而不是同时执行。此算法为[0,37]和[1,0]生成相同的结果。请注意,新实现仍然会为表单中的任何一对点生成冲突(x,37y),(y,37x)…+1有趣的是,Netbeans提出了与eclipse不同的建议,但实现的基础是相似的和可靠的。我很困惑,第二个实现如何是一个好的实现?即使是非常小的数字,比如(0,31),(1,0)也会发生冲突.这似乎非常有害,不是吗?@ChristopherShroba你的评论非常有趣,我度假回来后会调查一下!主要问题是,result
在给定示例输入的情况下是用0
初始化的。不过,IntelliJ 2016就是这样做的……对,表单的任何点都会发生冲突(a,31b),(b,31a)。感谢您的回复!我很想在您有机会的时候听听您对此的看法!@ChristopherShroba我认为问题是由0初始化的结果引起的,这也是错误的…但是,Netbeans实现也不能免疫(a,71b),(b,71a)
攻击…想法?也许您写错了“Java”而不是“Eclipse”。默认情况下,hashCode
“通常通过将对象的内部地址转换为整数来实现。”@MatthewFlaschen确实是这样做的。现在更新了;感谢您捕捉到了这一点。仍然容易受到(x,31y),(y,31x)
形式的任何对的攻击,例如(0,31),(1,0)或(3,217),(7,93)。我试图在这里引发一个问题范围的讨论。有没有一种方法可以实现更健壮的实现,或者只使用2个整数来处理此类问题(这取决于哈希代码生成中使用的素数)?向所有使用此标识的人发出警告。哈希冲突是现实情况…例如,pastebin.com/6wM3W3Wv
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
import java.util.Objects;
...
@Override
public int hashCode() {
return Objects.hash(x, y);
}
import com.google.common.base.Objects;
....
@Override
public int hashCode() {
return Objects.hashCode(x, y);
}
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
@Override
public int hashCode() {
int ax = Math.abs(x);
int ay = Math.abs(y);
if (ax>ay && x>0) return 4*x*x-3*x+y+1;
if (ax>ay && x<=0) return 4*x*x-x-y+1;
if (ax<=ay && y>0) return 4*y*y-y-x+1;
return 4*y*y-3*y+x+1;
}