假设我正在构建一个对象的TreeSet,其排序仅依赖于一个值.

我做不到

TreeSet<Foo> tree = new TreeSet<>(Comparator.comparingInt(Foo::getX));

因为如果我用相同的x添加两个不同的Foo对象,则其中一个将替换另一个(即,如果我添加tree.add(foo1)tree.add(foo2),tree.size()将是1而不是2).

我可以比较Foo的每个字段,但我希望Foo的两个实例被认为是不同的,即使每个字段都是相同的.

一种"几乎奏效"的解决方案是

TreeSet<Foo> tree = new TreeSet<>(Comparator.comparingInt(Foo::getX).thenComparing(Foo::hashCode));

但当存在散列冲突时,这将失败.

In summary,我正在寻找类似的东西

TreeSet<Foo> tree = new TreeSet<>(Comparator.comparingInt(Foo::getX).thenComparing(Foo::getInternalAddress));

但当然,我们无法使用这种方法.

变通办法

我知道有一些变通办法:

  • 如果我不关心对象本身,而只关心树中有多少个对象,我可以使用多集TreeMap<Foo, Integer>(并比较所有字段),从而得出特定x有多少个Foo
  • 如果我确实关心对象(我正在进行引用相等性判断),我可以使用不同的多集TreeMap<Foo, List<Foo>>(或键为xTreeMap<Integer, List<Foo>>).但是,如果只有很少的"重复"foo,这是对所有单例列表所占用空间的浪费.

因此,虽然我知道有TreeMap分的变通办法,但我仍然在想,是否有办法只用TreeSet分就可以做到这一点.

推荐答案

番石榴提供Ordering.arbitrary()个,你可以用它来构建你想要的Comparator:

Comparator.comparingInt(Foo::getX).thenComparing(Ordering.arbitrary())

Ordering.arbitrary()在内部使用System.identityHashCode,其中almost解决了您的"任意顺序,但考虑身份"问题,只是不能保证identityHashCode永远不会冲突(很简单,因为它是一个32位的值,并且一个Java项目可以很容易地创建2个以上的32对象,只要有足够的内存).

Ordering.arbitrary()使用一些巧妙的技巧来以一致(但任意!)的方式对具有冲突的identityHashCode值的对象进行排序,请参见its code.

Java相关问答推荐

Java字符串常数池困惑

Javascript更新alert 可扩展内容样式与CSS—按钮更多/更少

Java WireMock定义存根在Cucumber并行执行的多线程测试中失败

Select 按位运算序列

路径映射未发生

流迭代列表<;对象>;上的NoSuchElementException

现场观看Android Studio中的变化

在运行MVN测试时,为什么构建失败,并显示了java.lang.ClassNotFoundException:java.net.http.HttpResponse?

未找到适用于响应类型[类java.io.InputStream]和内容类型[Text/CSV]的HttpMessageConverter

基于配置switch 的@Controller的条件摄取

如何使用MapStrCut转换双向链接

在应用程序运行时更改LookAndFeel

在整数列表中查找和可被第三个整数整除的对时出现无法解释的RunTimeError

循环不起作用只有第一个元素重复

如何利用OpenTelemeter将初始值(零)输出到普罗米修斯

当使用不同的参数类型调用时,为什么围绕Objects.equals的类型安全包装不会失败?

如何使用stream.allMatch()为空流返回false?

类型安全:从 JSONArray 到 ArrayList> 的未经判断的转换

声纳覆盖范围为 0%,未生成 jacoco.xml

在数组列表中找到对象后,未从数组中删除对象