设计一个Point类
设计一个Point类
情景见:https://www.cnblogs.com/jiading/articles/13200172.html ,其实在图问题里还是很常见的需求
static private class Point {
Integer x, y;
Point(Integer x, Integer y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object obj) {
if(obj==this) {
return true;
}
if(obj==null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
Point objPoint = (Point) obj;
return this.x.equals(objPoint.x) && this.y.equals(objPoint.y);
}
@Override
public int hashCode() {
/*
* int hash = 7; hash = 71 * hash + x; hash = 71 * hash + y; if(hash<0) {
* hash=hash*-1; } return hash;
*/
int result = x;
result = 31 * result + y;
return result;
}
@Override
public String toString() {
return "(" + x + "," + y + ")";
}
}
向哈希容器中填充自定义的类的对象时要注意,一定要重写hashcode和equals方法!
-
hashcode:哈希容器将以此为哈希值选择元素填充位置。选择一个完美的hash function是很难的,但是我们也不需要这样,因为哈希冲突是被允许的,不会对结果造成影响,只是哈希冲突越少的话性能越好而已。对于图模型中常见的“点”类型,hash function的设计是被广泛讨论过的,可以看这里:https://stackoverflow.com/questions/9135759/java-hashcode-for-a-point-class 。有两个方法可以直接用,有论证它们的哈希冲突会比较少,但是我们就不论证了:
@Override public int hashCode() { int hash = 7; hash = 71 * hash + this.x; hash = 71 * hash + this.y; return hash; }@Override public int hashCode() { int result = x; result = 31 * result + y; return result; }第一个的话可以看到对x的放大倍数比较大,相对容易爆int,所以可以选择第二种方法。
-
如果说hash function的设计好坏不太重要,那么equals方法的设计就至关重要了。因为它直接决定了能不能在哈希容器中找到你要找的元素,也就哈希容器能否正常工作。对于点类,我们往往希望通过其坐标来判断点是不是相等,但是默认的判断方式是根据其对象的地址,所以需要重新equals方法,我们的目标是设计一个完备的equals方法,也即是和各种类型的对象都能正确比较的方法:
@Override public boolean equals(Object obj) { if(obj==this) { return true; } if(obj==null) { return false; } if (this.getClass() != obj.getClass()) { return false; } Point objPoint = (Point) obj; return this.x.equals(objPoint.x) && this.y.equals(objPoint.y); }- if(obj==this):这个其实是非必须的,但是作为一种特殊情况,把它放在第一句进行判断可以在有些时候避免无意义的运算,毕竟如果是一个对象的话坐标当然是相等的
- if(obj==null):这一句是必须的,因为null不属于任何类,它就是一个空指针,没有getClass方法,所以下面的通用判断方法不适用
- if (this.getClass() != obj.getClass()):如果这两个对象都不是一个类的,当然就不一样了
- 最后就是,既然判断都是点类的了,那么就可以把obj强转为点类,从而获取其坐标进行判断了,当然如果把不是点类的对象强转为点类会报错
我们一般只重写equals方法,而保留==运算符作为判断对象地址,从而判断是不是同一个对象的方法
有一个坑,就是整数常量池。我一开始大意了,最后一句写的是
return this.x==objPoint.x && this.y==objPoint.y;,测试的时候用小的值也没有问题,但是其实在大的值上是不对的,因为Java的整数有一个常量池,-128到127之间的整数都是用的同一个对象,所以是没问题的,但是超出这个范围就是新建的对象了,判断就是false了!这点一定要注意!java的常量池是一个坑点! -
另外,我也建议同时重写一下toString方法,这样print的时候会很方便,有些IDE也能在调试时直接显示toString方法的结果,很方便调试
@Override public String toString() { return "(" + x + "," + y + ")"; }

浙公网安备 33010602011771号