【问题标题】:Comparing objects in HashSet比较 HashSet 中的对象
【发布时间】:2018-11-22 21:53:24
【问题描述】:

我正在制作一个包含星星的 2D 游戏。我决定在名为 Star 的类中创建提供随机坐标的构造函数。

public Star(){
    super(0,0);
    x = randomX.nextInt(maxX - minX + 1);
    y = randomY.nextInt(maxX - minY + 1);
}

然后,在其他类中,我将它们放在 HashSet 中

Set<Star> star = new HashSet<>();

public Set<Star> generateStars(){
    while (star.size() < numberOfStars){
            star.add(new Star());
    }
    return star;
}

当然,我有 render 和 tick 方法,但我认为不值得粘贴它们。我的讲师告诉我,可以有相同的星星,为了防止我应该使用使用哈希码的身份函数。有人可以帮我弄清楚吗?我想这个函数应该检查哈希码是否相同,如果是这种情况,它应该只返回一个值,这样我们将在 HashSet 中添加 1 个对象而不是 2 个对象。我说的对吗?

【问题讨论】:

  • 同一颗星是什么意思?如果相等的标准是相同的坐标,只需根据坐标生成 hashcode 和 equals。如果没有,只需选择适当的相等标准并根据它覆盖哈希码和相等。
  • 您的构造函数有一些问题,使用相同的RNG可能会更好。此外,您的 y 坐标正在使用 maxX 变量。
  • 是的,我的意思是坐标。
  • 你需要创建一个 hashCode 和 equals 方法。您可以使用 IDE 生成这些。

标签: java collections


【解决方案1】:

Star 类中单独覆盖hashCode() 方法将不起作用您必须覆盖equals() 方法。

请参阅以下代码,其中我们不覆盖 equals() 方法:

class Star {
    int x, y;

    public Star(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}

public class Main {
    public static void main(String[] args) {
        Star s1 = new Star(0, 0);
        Star s3 = new Star(0, 0);
        Star s2 = new Star(31, -31*31);
        Set<Star> set = new HashSet<>();
        set.add(s1);
        set.add(s2);
        System.out.println(set.size());
    }
}

这将打印 3(而不是您预期的 2)。

原因是java.util.Set的add方法是基于equals()方法比较2个对象,而不是基于hashCode()方法。

Star 类的上述代码中,如果添加equals() 方法,现在输出将为2。供您参考,您可以覆盖equals() 方法,如下所示:

@Override
public boolean equals(Object startObject) {
    if (this == startObject) return true;
    if (startObject == null || getClass() != startObject.getClass()) return false;
    Star star = (Star) startObject;
    return x == star.x &&
            y == star.y;
}

那为什么还要加hashCode()呢?

  1. 当您使用HashSet 时,幕后的add 方法将调用equals() 方法,并且还将调用hashCode() 来决定应该将新对象放入的存储桶。为了维护hashCode()equals() 的合同,两者都应该被覆盖。
  2. 当您覆盖equals() 时,建议也覆盖hashCode()。 (反之亦然)。详情见this链接。

hashCode()equals() 的合同:如果对于两个对象来说 o1o2o1.equals(o2)true,那么 o1o2 的哈希值should 相同。

请确保您正确理解这一点,从上面的陈述中并不暗示如果 2 个对象的哈希值相同,则 o1.equals(o2) 应该返回 true可能对于 2 个对象,它们的哈希值相同,但 o1.equals(o2) 返回 false

参见hereObjecthashCode() 方法保证了什么。

请参阅this 链接以获取有关此主题的更多详细信息。

【讨论】:

    【解决方案2】:

    在 java 中,当向 HashSet 添加对象时,add 方法使用“equals”方法,该方法是 Object 类的一部分(但是您可以覆盖它)以确定集合是否已经包含您的对象正在尝试添加,请参阅:https://docs.oracle.com/javase/7/docs/api/java/util/HashSet.html

    但是,如果您要重写 equals 方法,那么您还应该重写 hashCode 方法,这在下面的帖子中有很好的解释:Why do I need to override the equals and hashCode methods in Java?

    如果您要遵循此建议,我还建议您使用 ApacheCommonsLang EqualsBuilderHashCodeBuilder(如果您的讲师允许),因为它们根据“有效 Java”一书中规定的规则提供了可靠的实现约书亚·布洛赫

    要覆盖 Star 类中的 equals 方法,您需要考虑使两个星形对象相等的标准。从您的示例来看,这可能是它们都具有相同的 x 和 y 坐标。

    【讨论】:

      【解决方案3】:

      您可以覆盖 Object.hashCode() 方法。因此,在您的 Star 类中,添加:

      @Override
      public int hashCode() {
          in hash = .... //make your hash code about the coordinates of your star.
          return hash;
      }
      

      因此,当您在哈希图中放置具有相同坐标的新星时,如果地图中已经存在相同坐标,它将覆盖先前具有相同坐标的开始。

      【讨论】:

        【解决方案4】:

        您必须实现hashCodeequals。 代码应该是这样的:

          public static class Star {
            int x, y;
            public Star(int x, int y) {
              this.x = x;
              this.y = y;
            }
        
            @Override
            public int hashCode() {
              return x * 1000 + y;
            }
        
            @Override
            public boolean equals(Object obj) {
              if (obj instanceof Star) {
                Star s = (Star) obj;
                return this.x == s.x && this.y == s.y;
              }
        
              return false;
            }
        
          }
        
          public static void main(String args[]) throws Exception {
            HashSet<Star> set = new HashSet<Star>();
            set.add(new Star(1, 1));
            set.add(new Star(1, 1));
        
            System.out.println(set.size());
          }
        

        注意:选择适合您的哈希码函数。我在这里假设 y 应该始终小于 1000。

        【讨论】:

        • 哈希集中有冲突是可以的。您是否保证 String.hashcode 为世界上每个可能的字符串返回不同的哈希码?这就是我们需要equals方法的原因!!
        • 请为 hashcode 方法建议不同的实现方式?
        • 我为 hashcode 添加了不同的实现,但我仍然不能保证所有可能的对象都会有不同的 hashcode。
        • 为不相等的对象生成不同的哈希结果将提高哈希集合的性能。如果对象不相等,您的目标应该是产生不相等的散列。这允许容器在所有可能的哈希值上均匀分布不相等实例的集合。我建议一些形式的散列:h(star) = m * star.x + star.y + c 用于一些标量m 和偏移量c。偏移量c 对于区分星形(0,0)null 很重要。用于常量的值是任意的。
        【解决方案5】:

        如果您使用 eclipse 作为您的编辑器,只需右键单击编辑器窗格,转到源代码然后生成 hashcode() 和 equals() 选择您想要考虑的参数。你会得到一个自动生成的函数。

        【讨论】:

          猜你喜欢
          • 2023-03-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-12-27
          • 2010-11-14
          • 1970-01-01
          • 2013-11-01
          相关资源
          最近更新 更多