【问题标题】:TreeSet/TreeMap equivalent for HashSet/HashMap (custom hasher)TreeSet/TreeMap 等效于 HashSet/HashMap(自定义哈希)
【发布时间】:2012-01-22 01:28:53
【问题描述】:

TreeSet 有一个接受比较器的构造函数,这意味着即使您存储的对象本身不是Comparable 对象,您也可以提供自定义比较器。

有无序集的类似实现吗? (例如,HashSet<T> 的替代方案采用“哈希”对象,该对象为可能与对象自己的实现不同的对象 T 计算 equals()hashCode()?)

C++ std::hash_set 给你这个,只是想知道是否有一些适用于 Java 的东西。


编辑:@Max 提出了关于equals() 的一个很好的技术观点——很公平; TreeMapHashMap 键通过 Map.containsKey() 也是如此。但是是否还有其他知名的数据结构允许通过自定义散列器进行组织?

【问题讨论】:

  • 顺便问一下,你确定你没有混淆不同的对象域吗?通常,将新方法添加到应用程序域中的对象中没有问题。但是,如果您尝试制作从某些生成的 Axis 客户端(例如)接收到的对象的映射,那么您正在混合不同的域 - WebService 的域和您的应用程序的域。这意味着,从本质上讲,您永远不需要您所要求的。

标签: java map set hashtable


【解决方案1】:

不,Collections 规范不支持“哈希”对象。您当然可以实现自己的支持此功能的集合,但另一种方法是将Hasher 视为您存储在HashSet 中的包装对象。

Set<HasherWrapper<Foo>> set = new HashSet<HasherWrapper<Foo>>();
set.add(new HasherWrapper(foo));
...

包装类看起来像这样:

private class HasherWrapper<T> {
    T wrappedObject;
    public HasherWrapper(T wrappedObject) {
        this.wrappedObject = wrappedObject;
    }
    @Override
    public int hashCode() {
        // special hash code calculations go here
    }
    @Override
    public boolean equals(Object obj) {
        // special equals code calculations go here
    }
}

【讨论】:

  • 很好地解决了这个问题。一个建议,你应该将wrappedObject 定为最终。此外,wrappedObject 应该是带有 getter 的 public 或 private。
  • 为什么是最终的?这对热点真的有那么大的帮助吗?鉴于它是一个私有类,我将允许外部类只访问该字段而无需 getter。
  • 许多网络参考资料说现在不需要 final ,因为虚拟机很聪明并且知道是否有某些东西被覆盖了。 stackoverflow.com/questions/4279420/…
【解决方案2】:

标准库中没有这样的实现,但它不妨碍你自己滚动。这是我经常想要拥有的东西。

原因见http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4771660

我们想避免复杂性。我们认真地接受了这个想法 当时设计了集合框架,但拒绝了它。这 功率重量比似乎很低。我们觉得equals就是你 95% 的时间都想要; ==, 4%;还有其他1%。写得懂 当相等谓词非常棘手时,批量操作的合同 不同。

【讨论】:

    【解决方案3】:

    不,没有,也不可能有规范。此外,您误解了TreeSet 使用它的方式Comparator

    来自TreeSet Javadoc

    请注意,由集合维护的顺序(无论是否显式 提供了比较器)必须与equals一致,如果它是 正确实现 Set 接口。 (见可比较或比较 用于与等于一致的精确定义。)就是这样 因为 Set 接口是根据 equals 操作定义的, 但是 TreeSet 实例使用它的执行所有元素比较 compareTo(或比较)方法,因此两个元素被视为相等 通过这种方法,从集合的角度来看,它们是相等的。这 一个集合的行为是明确定义的,即使它的顺序是不一致的 等于;它只是不遵守集合的一般合同 界面。

    来自Comparable javadoc

    C 类的自然排序据说是与 当且仅当 e1.compareTo(e2) == 0 具有相同的布尔值时等于 作为 e1.equals(e2) 对于 C 类的每个 e1 和 e2。注意 null 不是 任何类的实例,并且 e.compareTo(null) 应该抛出一个 NullPointerException 即使 e.equals(null) 返回 false。

    来自Collection javadoc

    布尔包含(对象o)

    如果此集合包含,则返回 true 指定的元素。更正式地说,当且仅当 集合至少包含一个元素 e 使得 (o==null ? e==null : o.equals(e))。

    因此,按照规范,不可能有任何类实现Collection&lt;E&gt; 接口,并且完全 依赖于一些外部比较器样式的对象来插入对象。所有集合都应使用Object 类的equals 方法来验证对象是否已插入。

    【讨论】:

    • 我试图理解为什么您不能提供与 equals 一致的不同自定义比较器。 e2.compareTo(e1) 没有实现这一点(它只是颠倒了顺序,但相等的项目仍然相等)?
    • 因为Collection接口的规范将取决于命名Comparator的程序员实现。 Collection 接口明确指出,如果a.equals(b),它的所有 实现将认为对象相等。如果您实现某种忽略或部分忽略该规则的 Collection(如作者指定的情况) - 您将违反 Collection 接口的规范。即使您在文档中写道“通过的比较器必须与等于一致!!” - 规范仍将被破坏。
    【解决方案4】:

    绝对没有这样的东西,hashcode()equals() 是定义对象的属性,不应更改。它们定义了使对象彼此相等的原因,并且这不应与一组对象有所不同。做你所说的唯一方法是子类化对象并编写一个新的hashcode()equals(),只有当子类有一个定义变量应该添加除了超级类'hashcode()equals()。我知道这可能不是您的目标,但我希望这会有所帮助。如果您更多地解释您想要这个的原因,那么如果存在一个更好的解决方案,它可能有助于找到一个更好的解决方案。

    【讨论】:

    • 实际上,您决定如何在程序中进行散列和比较对象的方式不是定义对象的属性,而是定义程序的属性。想要使用不同的散列算法,你知道它更适合你的特定问题集,或者如果对象具有相同的名称、相同的 id 或其他东西,则认为它们相等是没有错的。这取决于程序,这是 Java 的设计还有很多不足之处。
    猜你喜欢
    • 2020-10-10
    • 1970-01-01
    • 2017-06-05
    • 2015-07-06
    • 2011-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-30
    相关资源
    最近更新 更多