即使重复,HashMap也会添加所有项目

时间:2014-10-04 17:00:50

标签: java hashmap

我一直在努力想要制作模型中所有点的列表。当我执行此

    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;
        }       
    }
}

3 个答案:

答案 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()检查 - 这将有助于防止有人引入可变子类。 (无论如何,跨越继承层次结构的平等关系通常是痛苦的。)