请在 2024 年 Clojure 状态调查! 中分享您的想法。

欢迎!请查看关于页面,了解更多关于这个工作方式的信息。

0 投票
Collections

c.c/hash 始终使用 hashCode 为 Java 集合生成哈希值,而 Clojure 集合使用 Murmur3,因此它们在比较时是不兼容的。

user=> (== (hash (java.util.ArrayList. [1 2 3])) (hash [1 2 3])) false user=> (= (java.util.ArrayList. [1 2 3]) [1 2 3]) true

解决问题的一种方法是在 Util/hasheq 中为 java.util.Collections 添加一个特殊案例,就像现在对字符串所做的那样。

关于这个话题在 Clojure 群组中的讨论链接:https://groups.google.com/forum/#!topic/clojure/dQhdwZsyIEw

43 个答案

0 投票

评论者:wagjo

同样的问题也存在于映射中,因此 hasheq 应该为 java.util.Map 也添加一个特殊案例。

0 投票

评论者:wagjo

添加了修复 j.u. Map,Set 和 List 问题的补丁。

0 投票

评论者:jafingerhut

添加补丁 clj-1372-2.diff,它与 Jozef Wagner 的 clj-1372.diff 相同,但还添加了一些在更改之前失败、更改之后通过的新的测试。

0 投票

评论由:alexmiller发布

我认为equiv/hasheq的合约定义比这更窄,仅适用于两个集合都是IPersistentCollection的情况。换句话说,我认为这既不是所需的也不是必需的。

请注意,这里的Java .equals/.hashCode合约确实是维护的 - 这些集合将比较为.equals()并且确实有相同的.hashCode().

0 投票

评论者:wagjo

如果没有补丁,以下声明将不成立:“如果两个对象通过c.c /=相等,则它们返回的c.c / hash的哈希值相同”。我们可以这样说,只有当两个对象都是'clojure'对象时才成立,但这违背了Clojure的互操作性原则(互操作性简单、快速、不出意外)。

0 投票

评论者:wagjo

这个错误的体现

user=> (assoc (hash-map [1 2 3] :foo) (java.util.ArrayList. [1 2 3]) :bar) {[1 2 3] :bar, [1 2 3] :foo} user=> (get (hash-map [1 2 3] :foo) (java.util.ArrayList. [1 2 3])) nil

0 投票

评论由:alexmiller发布

我同意,这是一个非常好的说法,无需任何限制。

在hasheq中添加更多分支确实会有实际成本 - 添加这些集合检查会影响每一个hasheq。在运行完整的Clojure构建时,我看到了以下这些类与其发生关联的集合中的类(注意,其中没有任何一个是Java集合 - 这种情况本身就不存在于Clojure的构建中)

clojure.lang.Var 107001502 java.lang.Class 2651389 java.lang.Character 2076322 java.util.UUID 435235 java.util.Date 430956 clojure.lang.Compiler$LocalBinding 116830 java.lang.Boolean 112361 java.util.regex.Pattern 325

我们需要在每一个hasheq的路径中添加4个额外的instanceof检查。这也可能会破坏任何JVM内联。

Rich说“对于非值类型的hasheq/equiv,所有假设都应该取消”,显然Java集合就属于“非值”类别。

0 投票

评论者:jafingerhut

是否应考虑文档补丁?比如说,修改clojure.core/hash的文档,加入一个语句指示它只保证与不可变值时的clojure.core/=保持一致?也许还可以提到浮点数也不行:参见CLJ-1036

0 投票

评论由:alexmiller发布

我认为最好在https://clojure.org/data_structures中详细说明hash相关内容,而不是在文档字符串中。尽管hash的文档字符串可能需要更新,并指向最新变更后的网站。

0 投票

评论者:wagjo

尽管这是一个从1.5版本开始的重大变更,但在变更日志中应提及。仍然困扰我的问题是c.c/=在这种情况下被支持,但c.c/hash却没有。如果支持c.c/hash代价太高,难道不是更好地在这种情况下停止对c.c/=的支持吗?这将消除诸如

user=> (apply distinct? (hash-set [1 2 3] (java.util.Collections/unmodifiableList [1 2 3]))) false

0 投票

评论由:alexmiller发布

我不确定如果某项被认为不可保证更改的内容,这不是一个“破坏性”的变更。 :) 但我明白你的观点。

我不认为放弃Clojure和Java集合的=支持是可行的 - 这似乎很重要且有用。如果这样做是无代价的,我想能够无保留地说,如果equiv=true,则hasheq是相同的。

我对此票上的示例是否真正反映用户可能遇到的实际问题并不清楚。hasheq的主要用户是哈希表和哈希集合。因此,要显式化,你需要将Clojure和Java集合的混合体放入其中,特别是比较为相等的集合混合体。

仍在考虑。

0 投票

评论者:wagjo

我为垃圾邮件道歉,但可能还有另一种选择,即在hasheq中不回退到hashCode,而是在请求非值时抛出异常。这将导致hash类型更清晰的分离。当然这样可以防止非值放入hash集合中。

0 投票

评论由:alexmiller发布

但是没有简单的检查“值存在性”吗?

0 投票

评论者:jafingerhut

这是一个想法,或许值得考虑:在Util.hasheq方法中对java.util.Collection的实例进行一次测试,而不是对Set、List、Map分别进行三次测试。这没有涵盖Map.Entry。

0 投票

评论由:alexmiller发布

Map也没有扩展Collection。

...