使用UUID进行廉价的equals()和hashCode()

时间:2012-11-15 10:58:01

标签: java performance equals hashcode

我有一个不可变的类TokenList,它由Token对象列表组成,它们也是不可变的:

@Immutable
public final class TokenList {

    private final List<Token> tokens;

    public TokenList(List<Token> tokens) {
        this.tokens = Collections.unmodifiableList(new ArrayList(tokens));
    }

    public List<Token> getTokens() {
        return tokens;
    }
}

我在这些TokenLists上做了几个操作,它们将多个TokenLists作为输入并返回一个TokenList作为输出。可以有任意数量的TokenLists进入,每个TokenLists可以有任意多个Tokens。

这些操作很昂贵,并且很有可能多次执行相同的操作(即相同的输入),所以我想缓存输出。但是,性能至关重要,我担心在这些可能包含任意多个元素的对象上执行hashCode()和equals()的费用(因为它们是不可变的,然后hashCode可以被缓存,但是equals仍然很昂贵)。

这让我想知道是否可以通过对TokenList进行以下更新来简单而廉价地使用UUID来提供equals()和hashCode():

@Immutable
public final class TokenList {

    private final List<Token> tokens;
    private final UUID uuid;

    public TokenList(List<Token> tokens) {
        this.tokens = Collections.unmodifiableList(new ArrayList(tokens));
        this.uuid = UUID.randomUUID();
    }

    public List<Token> getTokens() {
        return tokens;
    }

    public UUID getUuid() {
        return uuid;
    }
}

这样的东西充当缓存键:

@Immutable
public final class TopicListCacheKey {

    private final UUID[] uuids;

    public TopicListCacheKey(TopicList... topicLists) {
        uuids = new UUID[topicLists.length];
        for (int i = 0; i < uuids.length; i++) {
            uuids[i] = topicLists[i].getUuid();
        }
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(uuids);
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) return true;
        if (other instanceof TopicListCacheKey)
            return Arrays.equals(uuids, ((TopicListCacheKey) other).uuids);
        return false;
    }
}

我认为有2 ^ 128个不同的UUID,我可能会在应用程序中随时拥有最多1,000,000个TokenList对象。鉴于此,以及UUID在缓存键中组合使用的事实,似乎产生错误结果的可能性非常小。尽管如此,我对它的进展感到不安,因为它感觉“很脏”。有什么理由我不应该使用这个系统吗? UUID.randomUUID()使用的SecureRandom的性能成本是否会超过收益(特别是因为我希望多个线程同时执行此操作)?碰撞是否比我想象的更可能?基本上,这样做有什么不妥吗?

感谢。

2 个答案:

答案 0 :(得分:3)

你正在尝试的是非常棘手的,需要详细的分析。因此,在决定任何方法之前,您需要检查以下问题。

  

这些操作很昂贵,并且很有可能多次执行相同的操作(即相同的输入)

1)当您在上面的行中说“相同输入”时,您的意思是什么?这是否意味着,完全相同的对象,即通过多个引用(相同的内存位置)引用的一个对象,或者它是指内存方式分离的对象,但逻辑上具有相同的数据?

这里如果对象是相同的,即相同的内存位置,那么==比较就可以了。为此,您必须将对象引用保留为缓存中的键。

但是,如果它是第二种情况,即内存方式分离的对象,但在逻辑上相同,那么我认为UUID不会帮助你。因为您必须确保这两个单独的对象将获得相同的UUID。这不会太容易,因为无论如何你必须通过整个TokenList数据来确保这个

2)在缓存中使用哈希码,是否安全?我建议不要使用hashcode作为密钥,因为即使2个对象不同,它们也可能具有相同的哈希码。所以你的逻辑可能会非常错误。

因此,首先要清楚地了解这些问题的答案。然后才考虑方法。

答案 1 :(得分:0)

SecureRandom不会给你任何提升,它比普通Random“更”随机。碰撞的可能性是数字平方除以总可能的UUID的顺序,因此数量非常小。不过,我不会依赖这个数字总是独一无二的。您可以尝试这样做,但最好检查并确保该数字尚未包含在哈希码列表中的其他位置。否则,你可能会遇到一些非常奇怪的问题......