7

我有一个不可变类 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 作为输出。可以有任意多个 TokenList 进入,每个 Token 可以有任意多个 Token。

这些操作很昂贵,并且很有可能多次执行相同的操作(即相同的输入),所以我想缓存输出。但是,性能至关重要,我担心在这些可能包含任意多个元素的对象上执行 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 的性能成本是否会超过收益(特别是因为我希望多个线程同时执行此操作)?碰撞会比我想象的更有可能吗?基本上,这样做有什么问题吗?

谢谢。

4

2 回答 2

3

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

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

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

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

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

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

因此,首先要弄清楚这些问题的答案,然后再考虑方法。

于 2012-11-15T11:28:46.360 回答
0

SecureRandom不会给你任何提升,它只是比普通的“更多”随机Random。发生冲突的可能性大约是数字的平方除以可能的 UUID 总数,所以这个数字非常小。不过,我不会依赖数字始终是唯一的。您可以尝试这样做,但最好检查并确保该数字尚未包含在哈希码列表的其他位置。否则,你可能会让自己陷入一些非常奇怪的问题......

于 2012-11-15T11:23:55.553 回答