对象上的Java任意比较器

标签 java comparator treeset

假设我正在构建一个 TreeSet排序仅取决于一个值的对象。

我做不到

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

因为如果我添加两个不同的 Foo具有相同 x 的对象然后一个将替换另一个(即,如果我做 tree.add(foo1)tree.add(foo2)tree.size() 将是 1 而不是 2 )。

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

“几乎可行”的一个解决方案是

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

但是当存在哈希冲突时这会失败。

总而言之,我正在寻找类似于

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

但是我们当然无法访问这样的方法。

解决方法

我知道有解决方法:

  • 如果我不关心对象本身,只关心树中有多少对象,我可以使用多重集 TreeMap<Foo, Integer> (并比较所有字段)给我多少 Foo s 用于特定的 x
  • 如果我确实关心对象(我正在做引用相等性检查),我可以使用不同的多重集 TreeMap<Foo, List<Foo>> (或 TreeMap<Integer, List<Foo>> 键为 x )。但是,如果“重复”的 foo 很少,那么所有单例列表都会浪费空间。

所以虽然我知道 TreeMap 有变通办法, 我仍然想知道是否有办法只用 TreeSet 来做到这一点.

最佳答案

Guava 提供 Ordering.arbitrary()你可以用它来构造你想要的 Comparator:

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

Ordering.arbitrary() 在内部使用 System.identityHashCode几乎 解决了您的“任意顺序,但考虑身份”问题,除了有不能保证 identityHashCode 永远不会发生冲突(很简单,因为它是一个 32 位值,并且 Java 项目可以轻松创建超过 232 个对象,只要有足够的内存)。

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

关于对象上的Java任意比较器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73925525/

相关文章:

java - 如何将两个不同的对象放入一个 TreeSet 中?

具有相等项的集合中的 Java 比较器

java - 通过 TreeSet 调用 iterator.next() [来自 Thinking In Java 的示例]

java - 当按下键盘上的某个键时,如何更改按钮的外观。 JavaFX

java - Android:为什么我不能将 setText 与 findViewById 放在同一行

Java - 性能问题;变量名和文件名长度

java - 如何对包含整数的 ArrayList<String> 进行排序?

java - TreeSet 自定义比较器算法 .. 字符串比较

java - 使用比较器将列表添加到树集中

java - Maven 未能通过 JUnit Spring Controller 测试,但在 Eclipse 中工作正常