Java HashMap添加所有项目,即使重复
我一直试图在没有任何运气的情况下列出模型中的所有点。当我执行此命令时Java HashMap添加所有项目,即使重复,java,hashmap,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)); poin
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
在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;
}
}
正如rolfl所注意到的,尝试使用java equals方法来比较具有可接受错误的点是没有用的(如果您有疑问,请参阅下面的TL/DR部分) 所以,你必须用艰难的方式去做。甚至不要想象使用
map.containsKey(e)
,而是在HashList
中创建一个显式方法。我将从该界面开始:
public interface DistEquality<E> {
boolean distEquals(E compare);
}
并以这种方式修改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++;
}
}
...
}
但是正如rolfl所注意到的,这是不够的,因为javadoc forObject.hashCode()
声明如果根据equals(Object)方法两个对象相等,那么对这两个对象中的每一个调用hashCode方法必须产生相同的整数结果
很难想象一个与上述相等方法兼容的智能哈希。更糟糕的是,一旦一个点得到一个hashCode值,任何其他点都处于有限数量的defaultError
,您可以想象一组完整的点,它们都是2/2相等的,因此hashCode应该是一个常量
更糟糕的是,由于要求equals是自反的、对称的和传递的,所以所有的点都应该是相等的
这样使用equal的想法似乎真的是一个不好的想法:-(如果您希望哈希表能够包含重复项,那么哈希表实现是不正确的。在添加时,如果映射已经有键,您只需返回值…,这样您的重复项就永远不会被插入 若你们得到的是重复的,那个就意味着你们点的Equals/HashCode很可能搞糟了
现在,你的HashList实际上不允许重复,所以你最好去掉它,使用java集合中的HashSet。另外,获得一个像样的java IDE,比如netbeans、eclipse、IntellIJ,让它为你的point类生成equals/hashcode。问题在于
equals(Object)中的最后块
。它总是返回false
,即使您从try
块返回true
你会因此感到困惑:
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()
复选框,这将有助于防止有人引入可变的子类(继承层次结构中的相等关系通常都很痛苦)
为什么不使用集合
?除此之外,在equals
方法中使用try/finally
是很可怕的。请使用instanceof
来代替。接下来,请提供一个简短但完整的程序来演示问题。我们不应该开始添加代码来创建相等点等。(请注意,Point
中的几乎所有代码都与问题无关,因为您没有旋转任何点等。)这不会起作用!hashCode()方法使用双对象的hashCode(),即(即使值相等,对于每个双对象几乎都是唯一的。equals方法也存在同样的问题,在这里比较对象实例,而不是值。使用双原语而不是对象。您需要比较像“if(Math.abs(a-b)<0.00001)”这样的值)'那么它们是相等的。我不知道如何从哈希值为int的double中构造哈希值。我建议使用使用使用equals方法的'TreeSet'或自定义comarator为什么不使用try来处理包含一些重复元素的较小数据子集并调试hashCode/equals方法?很可能您需要twea这只是部分答案。在不修改哈希代码的情况下修改equals将不起作用help@rolfl:您是对的,但即使equals方法也不尊重JDK契约-请参阅我的editI我对java是新手,但此修改是否会导致该类无法处理其他对象类型?该类是泛型的,应该是usab所有对象类型上的le。在这个特定实例中,我要查找的点与它们从同一个点集中产生的点完全相同(例如,立方体只有8个点,但需要36个点才能将其建模为三角形)一旦进入软件,所有点都应该以完全相同的方式进行操作,以便它们都应该继续具有完全相同的值,而不是近似值。这并不能回答为什么精确的双精度值仍然会导致重复。@MatthewCornelisse:Jon中给出了精确的双精度值仍然会导致重复的原因Skeet的回答。当您在类Point
中定义几乎相等的方法时,我试图想象如何使用它。修改后的类将适用于实现distEquals
方法的任何其他类。这意味着您可能需要包装现有类来实现它,或者更改逻辑并使用AlmostEquals比较器
类,如compare
可以使用Comparable
类或Comparator
。我不想得到重复项。问题是我得到了重复项,所以我在使用Eclipse如果你不想要重复项,那么你为什么不使用jus
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++;
}
}
...
}
public boolean equals(Object compare) {
return ((compare instanceof Point) ? equals(compare, defaultError) : false);
}
boolean equals=a.equals(b); //true
@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;
}