假设我正在构建一个 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/