我一直在努力想要制作模型中所有点的列表。当我执行此
HashList<Point> points=new HashList<Point>(16);
//add +y side
points.add(new Point(-5.0,5.0,-5.0));
points.add(new Point(-5.0,5.0,5.0));
points.add(new Point(5.0,5.0,5.0));
points.add(new Point(-5.0,5.0,-5.0));
points.add(new Point(5.0,5.0,5.0));
points.add(new Point(5.0,5.0,-5.0));
//add -x side
points.add(new Point(-5.0,5.0,-5.0));
points.add(new Point(-5.0,-5.0,-5.0));
points.add(new Point(-5.0,-5.0,5.0));
points.add(new Point(-5.0,5.0,-5.0));
points.add(new Point(-5.0,-5.0,5.0));
points.add(new Point(-5.0,5.0,5.0));
int length=points.length(); //equals 12, 6 expected
Point a=new Point(-5.0,5.0,-5.0);
Point b=new Point(-5.0,5.0,-5.0);
int aHashCode=a.hashCode(); //-737148544
int bHashCode=b.hashCode(); //-737148544
boolean equals=a.equals(b); //true
点数包含12点,这是我开始的数字。我希望找到所有重复项,这应该只会导致表中的6个点。
if (map.containsKey(e)) {
由于某种原因,HashList中的永远不会被执行。有什么想法吗?
HashList:
package dataTypes;
import java.util.ArrayList;
import java.util.HashMap;
public class HashList<E> {
private HashMap<E,Integer> map;
private ArrayList<E> data;
private int count=0;
public HashList() {
map=new HashMap<E,Integer>();
data=new ArrayList<E>();
}
public HashList(int size) {
map=new HashMap<E,Integer>(size);
data=new ArrayList<E>(size);
}
public int add(E e) { //returns key
if (map.containsKey(e)) {
return map.get(e);
} else {
map.put(e, count);
data.add(count,e);
return count++;
}
}
public int getKey(E e) {
return map.get(e);
}
public E get(int key) {
return data.get(key);
}
public int length() {
return count;
}
}
点:
package geometry3D;
/**
* 3D point location or vector
*
* @author Matthew Cornelisse
* @version 2014-09-02-004500
*/
public class Point
{
// instance variables - replace the example below with your own
public double x;
public double y;
public double z;
/**
* Constructor for objects of class Point
*/
public Point()
{
// initialise instance variables
x = 0;
y = 0;
z = 0;
}
public Point(double x, double y, double z)
{
this.x=x;
this.y=y;
this.z=z;
}
public Point(Point a) {
x=a.x;
y=a.y;
z=a.z;
}
/**
* Normailizes the point to have distance from center of 1
*
*/
public void normalize()
{
// put your code here
double length=Math.sqrt(x*x+y*y+z*z);
x/=length;
y/=length;
z/=length;
}
//implements Shape
public void rotateX(double angle){
double newY=Math.cos(angle)*y-Math.sin(angle)*z;
double newZ=Math.sin(angle)*y+Math.cos(angle)*z;
y=newY;
z=newZ;
}
public void rotateY(double angle){
double newX=Math.cos(angle)*x-Math.sin(angle)*z;
double newZ=Math.sin(angle)*x+Math.cos(angle)*z;
x=newX;
z=newZ;
}
public void rotateZ(double angle){
double newX=Math.cos(angle)*x-Math.sin(angle)*y;
double newY=Math.sin(angle)*x+Math.cos(angle)*y;
x=newX;
y=newY;
}
public void rotate(Vector axis, double angle){
//source: http://inside.mines.edu/fs_home/gmurray/ArbitraryAxisRotation/
double oldX=x;
double oldY=y;
double oldZ=z;
double sinA=Math.sin(angle);
double cosA=Math.cos(angle);
Point offset=axis.offset();
Point vector=axis.vector();
double u=vector.x;
double v=vector.y;
double w=vector.z;
double a=offset.x;
double b=offset.y;
double c=offset.z;
x=(a*(v*v+w*w)-u*(b*v+c*w-u*oldX-v*oldY-w*oldZ))*(1-cosA)+oldX*cosA+(-c*v+b*w-w*oldY+v*oldZ)*sinA;
y=(b*(u*u+w*w)-v*(a*u+c*w-u*oldX-v*oldY-w*oldZ))*(1-cosA)+oldY*cosA+(c*u-a*w+w*oldX-u*oldZ)*sinA;
z=(c*(u*u+v*v)-w*(a*u+b*v-u*oldX-v*oldY-w*oldZ))*(1-cosA)+oldZ*cosA+(-b*u+a*v-v*oldX+u*oldY)*sinA;
}
public void move(double x, double y, double z){
this.x+=x;
this.y+=y;
this.z+=z;
}
public void move(Vector direction,double magnitude){
this.x+=(direction.vector().x*magnitude);
this.y+=(direction.vector().y*magnitude);
this.z+=(direction.vector().z*magnitude);
}
public boolean equals(Point compare) {
if (Math.abs(compare.x-x)>5*Math.ulp(compare.x)) return false;
if (Math.abs(compare.y-y)>5*Math.ulp(compare.y)) return false;
if (Math.abs(compare.z-z)>5*Math.ulp(compare.z)) return false;
return true;
}
public boolean equals(Point compare, double error) {
if (Math.abs(compare.x-x)>error) return false;
if (Math.abs(compare.y-y)>error) return false;
if (Math.abs(compare.z-z)>error) return false;
return true;
}
public int hashCode(){
Double a=(Double)x;
Double b=(Double)y;
Double c=(Double)z;
return a.hashCode()^Integer.rotateRight(b.hashCode(),12)^Integer.rotateRight(c.hashCode(),24);
}
public boolean equals(Object compare) {
try {
Point temp=(Point)compare;
if (temp.x!=x) return false;
if (temp.y!=y) return false;
if (temp.z!=z) return false;
return true;
} finally {
return false;
}
}
}
答案 0 :(得分:1)
正如rolfl所注意到的那样,尝试使用java equals方法将Points与可接受的错误进行比较是没有用的(如果您有疑问,请参阅下面的TL / DR部分)。
所以,你必须以艰难的方式去做。甚至不要想象使用map.containsKey(e)
,而是在HashList
中创建一个显式方法。我会从那个界面开始:
public interface DistEquality<E> {
boolean distEquals(E compare);
}
然后声明Point
来实现它:
public class Point implements DistEquality<Point> {
...
public static double defaultError = 10E-3;
@Override
public boolean distEquals(Point compare) {
return equals(compare, defaultError);
}
}
以这种方式修改HashList
public class HashList<E extends DistEquality<E>> {
...
public int distValue(E e) {
for (Entry<E, Integer> entry: map.entrySet()) {
if (e.distEquals(entry.getKey())) {
return entry.getValue();
}
}
return -1;
}
public int add(E e) { //returns key
int pos = distValue(e);
if (pos != -1) {
return pos;
} else {
map.put(e, count);
data.add(count,e);
return count++;
}
}
...
}
我没有测试任何东西,但我认为一般的想法应该是好的。
TL / DR 以下解决方案是完全错误的 - (感谢rolfl注意到)
类equals
中的Point
方法要求完全相等的双精度数。您应该在static double defaultError
类中使用Point
,并以适当的值初始化,然后执行:
public boolean equals(Object compare) {
return ((compare instanceof Point) ? equals(compare, defaultError) : false);
}
但正如rolfl所注意到的,这还不够,因为Object.hashCode()
的javadoc状态如果两个对象根据equals(Object)方法相等,则在两个对象上调用hashCode方法对象必须生成相同的整数结果。
很难想象一个与上述相等方法兼容的智能哈希。更糟糕的是,一旦一个点获得一个hashCode值,任何其他点的有限数量为defaultError
,你可以想象一个完成的Point套件,每2个等于2,所以hashCode应该是一个常量。
更糟糕的是,由于平等要求具有反身性,对称性和传递性,所有点应该相等。
看起来使用相同方式的想法实际上是一个糟糕的想法: - (
答案 1 :(得分:0)
如果您希望HashList实现能够包含重复项,则该实现不正确。在你的添加中,如果地图已经有了密钥,你只需返回值...,这样你的副本就不会被插入。
如果你得到重复,那意味着你的Equals / HashCode for Point可能搞砸了。
现在,你的HashList实际上并不允许重复,所以你也可以摆脱它并使用java集合中的HashSet。另外,得到一个像netbeans,eclipse,IntellIJ这样体面的java IDE,让它在你的点类上为你生成equals / hashcode。
答案 2 :(得分:0)
问题在于finally
中的equals(Object)
阻止。即使您从false
块返回true
,总是返回try
。
你因此而感到困惑:
boolean equals=a.equals(b); //true
...但是这并没有调用相同的equals
方法 - 它正在调用equals(Point)
。
您的equals(Object)
方法应写为:
@Override public boolean equals(Object compare) {
if (compare == this) {
return true;
}
if (compare == null || compare.getClass() != getClass()) {
return false;
}
Point temp = (Point) compare; // Guaranteed to work now
return temp.x == x && temp.y == y && temp.z == z;
}
请注意:
final
,则可以使用instanceof
代替getClass()
检查 - 这将有助于防止有人引入可变子类。 (无论如何,跨越继承层次结构的平等关系通常是痛苦的。)