【问题标题】:HashSet does not seem to realize that two objects are the same.HashSet 似乎没有意识到两个对象是相同的。
【发布时间】:2010-09-11 19:50:37
【问题描述】:

我正在尝试使用 HashSet 来存储我创建的类的对象,但显然相同的对象似乎有两个不同的哈希值,这就是 contains 方法没有意识到该对象已经在 HashSet 中的原因。这会导致我的程序耗尽堆内存。

我不认为我做错了什么,但无论如何我想要第二个意见。我做过类似的操作,之前都运行良好,这使得这特别烦人。我会很感激任何帮助。

这是我的代码

move1 = new Move(t,s);
if(move1.hashCode()==new Move(t,s).hashCode())
    System.out.println("match");
move2 = new Move(s,t);
moves.add(move1); 
moves.add(move2);
if(moves.contains(new Move(t,s)))
    System.out.println("match found");

这是 Move 类:

public class Move {
    private int move1;
    private int move2;

    Move(int m1, int m2)
    {
        move1 = m1;
        move2 = m2;
    }

    public String toString()
    {
         return String.valueOf(move1)+" "+String.valueOf(move2);
    }
}

这是我得到的输出

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.HashMap.addEntry(HashMap.java:797)
    at java.util.HashMap.put(HashMap.java:431)
    at java.util.HashSet.add(HashSet.java:194)
    at makeMove.<init>(makeMove.java:33)

【问题讨论】:

  • 重新标记,因为异常是问题的副作用。

标签: java hashset


【解决方案1】:

您需要覆盖Move 类中的Object#hashCode() 方法,使其返回与Move 实例状态相同的hashCode() 值。不要忘记覆盖Object#equals()

另见:


提示:如果您使用像 Eclipse 这样的 IDE,您也可以只自动生成它们。右键单击Move 类的某处,选择Source > Generate hashCode() and equals()。下面是它的样子:

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + move1;
    result = prime * result + move2;
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Move other = (Move) obj;
    if (move1 != other.move1)
        return false;
    if (move2 != other.move2)
        return false;
    return true;
}

【讨论】:

  • 感谢您的提醒。特别是对于 Eclipse 提示。
  • 这一切都记录在 HashSet 的 Javadoc 中。这就是它的用途!
  • 太棒了。完美地工作。谢谢!
【解决方案2】:

HashSet 将根据调用 hashCode() 和 equals() 来确定相等性。您还没有实现这些,因此您将从 Object 继承它们。 Object 的 hashCode 和 equals 方法只是根据引用是否相等。

这就是为什么if(move1.hashCode()==new Move(t,s).hashCode()) 是假的。 move1 与调用 new Move(t,s).hashCode() 创建的实例不同

您需要在 Move 类中实现 hashCode 和 equals。

例如(虽然可能不是最佳的,并且您可能需要一个空安全等号 - 如果可以的话,让您的 IDE 生成它们)

public int hashCode() {
    return move1 ^ move2 +;
}

public boolean equals(Object o) {
  if(!other instanceof Move) 
      return false;

  Move other = (Move)o;

  return other.move1 == move1 && other.move2 == move2;
}

【讨论】:

    【解决方案3】:

    您必须覆盖 equals()hashCode()

    这可能是一个选项。

    import static java.lang.System.out;
    public class Move {
        private int move1;
        private int move2;
    
        Move(int m1, int m2) {
            move1 = m1;
            move2 = m2;
        }
    
        public String toString() {
             return String.valueOf(move1)+" "+String.valueOf(move2);
        }
    
        public int hashCode() {
            return move1 * 31 + move2 * 31;
        }
        public boolean equals( Object other ) {
            if( this == other ) { return true; }
            if( other instanceof Move ) {
                Move m2 = ( Move ) other;
                return this.move1 == m2.move1 && this.move2 == m2.move2;
            }
            return false;
        }
    
        public static void main( String  [] args ) {
            out.println( new Move(2,3).equals( new Move(2,3)));
            out.println( new Move(1,1).hashCode() == new Move(1,1).hashCode()  );
        }
    }
    

    您必须定义移动的顺序是否相关(1,2 是否等于 2,1)

    更多信息:

    What issues should be considered when overriding equals and hashCode in Java?

    【讨论】:

    • 您的哈希码不太正确。您不应该将这两个数字乘以 31 然后相加。您应该将 move1 乘以 31,然后将结果乘以 31 并添加 move2。
    • @EntangledLoops 错误,绝对不需要遵循任何特定算法来创建哈希码(参见docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/…)您描述的方式更受欢迎的事实与结果的正确性完全无关.只要符合上述规范,您可以使用任何其他数字和任何其他操作和顺序。我明白你从哪里来,但这与说我的例子“不太正确”和“我不应该这样做”是有区别的
    • 按照这个逻辑,让你的所有hashCode() 函数返回 1 是正确的。当然,你可以做你想做的事,但有一个原因通常是在考虑特定策略的情况下完成:因为它提供更多样化的分桶,这首先是散列的重点。您的代码示例使两个数字都是 31 的倍数,这意味着它们的总和也是 31 的倍数,这可能导致每个项目都散列到同一个存储桶中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多