设计一个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 + ")";
    		}
    
posted @ 2020-06-27 22:10  别再闹了  阅读(469)  评论(0)    收藏  举报