【问题标题】:Why doesn't Java have an interface similar to Comparator, but for hashing? [duplicate]为什么Java没有类似于Comparator的接口,而是用于散列? [复制]
【发布时间】:2013-03-05 12:59:46
【问题描述】:

在 Java 中,Comparator 接口允许客户端为任何类型指定 equals() 和 compare() 方法。 Comparator 可以传递给大多数(或所有)需要排序的集合,并且将使用 Comparator 中的方法而不是指定类的方法。这允许客户端以不同于其自然顺序的方式对对象进行排序,甚至可以对没有自然顺序的对象进行排序(即不实现 Comparable)。

为什么没有类似的哈希接口?它可以指定两种方法,hashCode() 和 equals(),并且对 HashSet 或 HashMap 有用,就像比较器对排序有用一样。

编辑:对于那些将这个问题标记为this other question 的重复项的人,我会提到另一个问题是为什么 hashCode 包含在每个类中而不是接口中,而这个问题是关于抽象散列函数以允许多个它的实现。

回答编辑:获得此功能的最佳方法似乎是:

-如果您可以使用外部库和/或已经在使用 Guava(由于很多原因,这是一个很棒的库),Guava 有一个 Equivalence 类允许这样做。

-如果您不想使用外部库,可以使用自定义构建的适配器,类似于this SO question 上的顶级答案中所做的。

【问题讨论】:

  • 首先,hashCode 是来自Object 的本机实现。这意味着它将始终在任何类中定义。 equals 也在 Object 中定义。这两者总是在任何情况下都被定义,因此为它们提供接口是没有意义的。
  • @Legend。当然,这可能是有道理的。在特定情况下,如果姓名相同,您可能希望将人视为平等,而在另一种情况下,如果他们具有相同的 SSN,您可能希望将他们视为平等。就像使用比较器一样,您可以将其外部化为“均衡器”。适配器模式可能是解决这个问题的最佳解决方案(诚然,比希望以不同方式对对象进行排序的频率要低得多)
  • 见 Guava 的Equivalence
  • 你说这没有意义。那是完全错误的。这说得通。番石榴创造了这样的东西。例如,它也有 Multimap。你是说 Multimap 没有意义,只是因为 Sun 没有在 JDK 中添加它?仅仅因为它对没有用并不意味着它没有意义。

标签: java collections hash comparator


【解决方案1】:

表格问题

“为什么Java没有XXX”

很难客观地回答,除非是通用的

“我们不知道,因为做出决定时房间里没有人。”

在这种情况下:

  • 从表面上看,这个要求是可以实现的……从技术角度来看。

  • 此要求已通过 RFE 多次提出。最直接的就是http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6435963。 RFE 被标记为 WONT FIX,但没有给出具体原因。

  • 第 3 方库可以并且已经充分满足此要求。

我对此的解读是它不受支持,因为对于足够多的人来说,它被认为不够重要以至于需要支持。我会说他们对此做出了合理的决定。

【讨论】:

  • 我同意您回答中从问题分析到阅读的所有内容。我主要想知道除了它在许多情况下没有用的事实之外是否还有其他原因。也感谢您提供相关 RFE 的链接。
  • “我主要想知道除了它在许多情况下没有用的事实之外是否还有其他原因。” - 这只是一个观察到的事实。在绝大多数情况下,关键对象的“自然”等于和哈希码是可以的。为什么?也许是因为人们不使用“时髦的钥匙”?或者因为如果确实使用“时髦的键”,那么他们使用 TreeMaps 而不是 HashMaps?
  • 在过去的美好时光中,您可以根据 RFE 在 Bug Parade 中的投票数粗略衡量 RFE 获得了多少支持。不幸的是,甲骨文决定他们不想再知道了。
  • 对于许多“为什么 X 不做 Y”形式的问题,有一个类似的问题“如果有人在设计类似于 X 的东西,让它做 Y 会更没用或更成问题比乍看之下的“?人们不必参与 X 的实施就可以回答后一个问题;在许多情况下,找出不应该做某事的非显而易见的原因可能非常有用,但在许多情况下,如果不知道问题是什么,就不可能知道一个问题是否会有有用的答案。答案是。
【解决方案2】:

我也认为这样的界面会很方便,特别是作为一种考虑集合中不同类型的平等的方式。它应该类似于创建对象Comparable 的方式,但仍通过提供其他一些Comparator 来覆盖特定集合中的该行为。

正如 this question 中所指出的,Guava 有一个 Equivalence 类,它提供了一种方法来做到这一点,通过包装你的类并让你在包装器级别定义在这种情况下“平等”的含义。

如果您的问题真的是为什么在语言设计时没有发生这种情况......好吧,嘿,James Gosling 和公司只是人类,对吧?

【讨论】:

    【解决方案3】:

    散列实际上只有一个要求:像散列方法一样。因此,您可以为一种类型实现它,而无需知道谁会以何种方式以及出于何种目的使用它。所以对象本身的hash方法就足够了。

    另一方面,Equals 在不同的上下文中具有不同的含义。例如,您可以按名字、姓氏、年龄、大小、体重、加入俱乐部的时间对人员进行排序……因此,对单个班级有不同的相等(和“小于”)实现是有意义的。

    当然,没有什么能阻止您创建和使用这样的界面...

    【讨论】:

    • hashCode()equals() 方法是不可分割的。如果一个类型对equals() 有多个有意义的含义,那么它通常对hashCode() 有同样多的有意义的含义。有可能构建一个hashCode() 实现,该实现对于多个equals 方法[e.g.区分大小写和不区分大小写的比较都可以使用从字符串的仅大写版本计算的哈希值],但这种方法通常不是最佳的。
    【解决方案4】:

    您可以随时更改 hashcode() 以说明 HashMap 如何在 Map 中排列您的对象,并通过在您的类中实现有效的 hashcode 方法来提高其性能。

    HashMap 如何从散列数据中执行添加和删除是 HashMap 内部的,更改它基本上意味着更改添加、删除方法等。

    排序也是一个常用功能,使用频率更高,在极少数情况下,当您真的想更改地图中散列的发生方式时,始终可以选择扩展地图。

    【讨论】:

      【解决方案5】:

      让我试着说说我是怎么看的,为什么。

      简而言之 - 您经常需要排序列表对象,但很少(如果有的话)需要交叉比较列表的身份 对象


      在 Object 中,方法 hashCode 是辅助的、有用的方法,其主要目的是服务于 equals 方法。约定是,如果对于两个对象 hashCode 返回不同的值,equals 一定不能返回 true。

      因此,hashCodeequals 方法用于确定对象的身份

      方法 compareTo(在 Comparable 和 Comparator 中)服务于另一个通用目的。它定义对象顺序,而不是它们的身份

      Resume - compareTo 定义了 objects 如何orderedhashCode(连同 equals) 定义对象身份


      再次 - 实践的贡献是您经常需要对一组 对象 进行排序,但很少(如果有的话)必须对一组 对象 进行交叉比较他们的身份

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-25
        • 2010-12-17
        • 2018-09-22
        • 1970-01-01
        相关资源
        最近更新 更多