比较HashSet中的对象

时间:2018-11-22 21:53:24

标签: java collections

我正在制作一个带有星星的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个对象。我说的对吗?

4 个答案:

答案 0 :(得分: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,因为它们基于书中列出的规则提供了可靠的实现, Joshua Bloch的《有效Java》

要覆盖Star类中的equals方法,您需要考虑使两个star对象相等的条件。从您的示例来看,这可能是因为它们的x和y坐标相同。

答案 1 :(得分:1)

在您的hashCode()类中单独覆盖Star方法是行不通的,您将不得不覆盖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()方法而不是基于hashCode()方法的2个对象。

在上述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()的合同:如果对于两个对象说o1o2,则o1.equals(o2)为{{1 }},则trueo1 o2的哈希值是相同的。

请确保您正确理解了这一点,从上面的陈述并不意味着如果2个对象的哈希相同,则should应该返回o1.equals(o2)有可能两个对象的哈希值相同,但是true返回o1.equals(o2)

请参见herefalse的{​​{1}}方法保证的内容。

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

答案 2 :(得分:0)

您可以从Object覆盖hashCode()方法。因此,在您的星级课程中,添加:

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

因此,当您在散列图中放置具有相同配角点的新星时,如果地图中已经存在相同星点,则它将覆盖之前的起点。

答案 3 :(得分:0)

您必须实现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。