实现Java对象标识的比较器

时间:2016-06-17 16:26:11

标签: java comparable

我有一个使用对象标识的令牌类(如equals中只返回tokenA == tokenB)。我想在TreeSet中使用它。这意味着我需要在两个与参考相等兼容的令牌之间进行比较。我不关心具体的实现,只要它与equals一致并履行合同(按照TreeSet:"请注意,如果要正确实现Set接口,则由set维护的排序(无论是否提供显式比较器)必须与equals一致。")

注意:这些令牌是在多个线程上创建的,可以在不同的线程上进行比较。

这样做的最佳方法是什么?

我尝试的想法:

  • 使用System.identityHashCode - 问题在于无法保证两个不同的对象始终具有不同的哈希码。由于生日悖论,你只需要大约77k令牌才会发生碰撞(假设System.identityHashCode均匀分布在整个32位范围内,这可能不是真的......)
  • 对每个令牌使用默认Object.toString方法的比较器。不幸的是,在引擎盖下,这只是使用哈希码(与上面相同)。
  • 使用int或long唯一值(读取:静态计数器 - >实例变量)。这会增加大小,并使多线程变得痛苦(更不用说使对象创建有效地单线程)(静态计数器的AtomicInteger / AtomicLong有点帮助,但它的大小膨胀更多在这里烦人)。
  • 对任何碰撞使用System.identityHashCode和静态消歧图。这有效,但相当复杂。此外,Java默认情况下没有ConcurrentWeakValueHashMultiMap(不是一口),这意味着我必须引入外部依赖(或者写我自己的 - 可能使用与this类似的东西来执行此操作,或遭受(缓慢)内存泄漏,或使用终结器(呃)。 (而且我不知道是否有人实施了这样的事情......)

顺便说一句,我不能解决这个问题并假设唯一对象具有唯一的哈希码。那就是我在做什么,但是在比较器中发出了断言,所以我挖了它,然后,在我的机器上看到以下内容:

import java.util.*;
import java.util.Collections.*;
import java.lang.*;

public class size {
    public static void main(String[] args) {
        Map<Integer, Integer> soFar = new HashMap<>();
        for (int i = 1; i <= 1_000_000; i++) {
            TokenA t = new TokenA();
            int ihc = System.identityHashCode(t);
            if (soFar.containsKey(ihc)) {
                System.out.println("Collision: " + ihc +" @ object #" + soFar.get(ihc) + " & " + i);
                break;
            }
            soFar.put(ihc, i);
        }
    }
}
class TokenA {

}

打印

Collision: 2134400190 @ object #62355 & 105842

所以碰撞确实存在。 那么,有什么建议吗?

1 个答案:

答案 0 :(得分:0)

没有魔法:

问题tokenA == tokenB比较身份,tokenA.equals(tokenB)比较.equals()中为该类定义的内容,无论身份如何。

因此,两个对象可以.equals()返回true而不是相同的对象实例,它们甚至不必具有相同类型或共享超类型。

没有捷径:

实现compareTo()是您要比较的任何对象的属性。您只需编写代码并使其按照您的意愿执行,但compareTo()可能您想要的内容。 compareTo()用于进行比较,如果您有两个事物以<>彼此以某种有意义的方式相互作用,那么ComparableComparator<T>就不是您想要的。

等同于身份很简单:

public boolean equals(Object o)
{
    return this == o;
}