Java:HashSet比较的概念是什么? 来自C++世界,我发现哈希集文档的阅读有些困难: 在C++中,你会有:

Java:HashSet比较的概念是什么? 来自C++世界,我发现哈希集文档的阅读有些困难: 在C++中,你会有:,java,set,language-concepts,Java,Set,Language Concepts,这反过来又指向: 这使得对std::set处理的元素类型的要求变得显而易见。我的问题是:Java中Set维护的元素类型(E)有什么要求 下面是一个我无法理解的简短示例: import gdcm.Tag; import java.util.Set; import java.util.HashSet; public class TestTag { public static void main(String[] args) throws Exception { Tag

这反过来又指向:

这使得对
std::set
处理的元素类型的要求变得显而易见。我的问题是:Java中
Set
维护的元素类型(E)有什么要求

下面是一个我无法理解的简短示例:

import gdcm.Tag;
import java.util.Set;
import java.util.HashSet;

public class TestTag
{
  public static void main(String[] args) throws Exception
    {
      Tag t1 = new Tag(0x8,0x8);
      Tag t2 = new Tag(0x8,0x8);
      if( t1 == t2 )
        throw new Exception("Instances are identical" );
      if( !t1.equals(t2) )
        throw new Exception("Instances are different" );
      if( t1.hashCode() != t2.hashCode() )
        throw new Exception("hashCodes are different" );
      Set<Tag> s = new HashSet<Tag>();
      s.add(t1);
      s.add(t2);
      if( s.size() != 1 )
        throw new Exception("Invalid size: " + s.size() );
    }
}
根据我对文档的阅读,仅需要为Set实现equals运算符:

我在文档中遗漏了什么

我在文档中遗漏了什么

您正在查看文档的错误部分

C++是一组“独特对象”,通常是“红黑树”。 在Java中,是一个更抽象的概念(它是一个接口,而不是一个类),具有多个实现,最显著的是和(忽略并发实现)

正如您可以从名字中猜到的,java <代码>树目录相当于C++ <代码> SET

对于需求,
HashSet
使用和方法。它们是在
对象
类上定义的,并且需要在需要位于或作为键位于的类上重写


对于和键,您有两个选项:在创建
树集
(类似于C++)时提供,或者让对象实现接口。

我只是试图重现您的问题,可能您没有正确重写equals和/或hashSet

看看我不正确的标签实现:

public class Tag {

private int x, y;

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

public boolean equals(Tag tag) {
    if (x != tag.x) return false;
    return y == tag.y;
}

@Override
public int hashCode() {
    int result = x;
    result = 31 * result + y;
    return result;
}
}
看起来很不错,不是吗?但问题是,我实际上没有重写正确的equals方法,而是用自己的实现重载了它

要正确工作,equals必须如下所示:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Tag tag = (Tag) o;

    if (x != tag.x) return false;
    return y == tag.y;
}

我猜这只是运气不好和对HashSet要求的误解的结合。感谢@christophe的帮助,我在尝试添加swig生成的Tag.java类时意识到了这个问题:

@Override
public boolean equals(Object o) {
}
我收到以下错误消息:

gdcm/Tag.java:78: error: method does not override or implement a method from a supertype
  @Override
  ^
1 error
1 warning
这意味着我的错误很简单:

  • 我一开始就有错误的签名:
    布尔等于(对象o)
    !=<代码>布尔等于(标记t)
提示只是使用
@Override
关键字


对于那些请求上游代码的人,Java代码由swig生成。原来的C++代码在这里:


每个对象都实现了
hashCode
,因为默认的impl存在于
object
中。这是否可以/应该优化是另一个问题。@malat你的例外毫无意义。如果<代码> > t1。等于(t2)< /> >和<代码> t1。HASCODE(=)=t2。HASCODE()/<代码> <代码> s siz()>代码>应为1,因为<代码> HasSET/<代码>将考虑<代码> T1和<代码> T2< /代码>相同。可能您提到的导致异常的代码不是您发布的代码。唯一的问题是,如果没有
标记
实现,您就不会有异常。由于这个原因,可以认为这个问题是偏离主题的,因为我不能复制你的问题。C++ <代码> set <代码>包含一个类型的唯一类型的键值对象。排序是使用键比较函数<代码>比较< /代码>。Java的等价物是(不是
HashSet
):元素按照其自然顺序排序,或者通过在集合创建时提供的
Comparator
排序,具体取决于使用的构造函数。正如@Eran所提到的,不可能得到您声明的与发布的代码相关的异常。除非你故意破坏了
equals
hashCode
,否则请发布你的实际代码。破坏它的好方法。我只是使用了一个布尔值,第一次返回true,然后返回false。事实上,我对
equals
函数使用了错误的签名,在我的例子中很难跟踪,因为我使用swig生成java代码。在java编程时,作为一般提示:始终使用@Override注释。如果您在将来的某个时候更改了一个超类的签名,然后突然发现子类的行为异常,这也会有所帮助。但是我现在要记住这个技巧:)谢谢你仔细阅读我的问题,事实上现在我看到了
equals()
方法的正确签名。我现在明白了在Java
Set
中实际上是
Set
,这对所有Java开发人员来说都是显而易见的(这解释了否决票),但是这对大多数C++程序员来说是违反直觉的。@马拉特的下投票(至少是我的)是因为你没有发布相关的代码,这会让你回答简单。“实际上是SET”,这远远不是正确的,java泛型比那时复杂得多,它们与C++模板非常不同。
gdcm/Tag.java:78: error: method does not override or implement a method from a supertype
  @Override
  ^
1 error
1 warning