【问题标题】:Why are two different java.util.UUID objects comparing as equal?为什么两个不同的 java.util.UUID 对象比较相等?
【发布时间】:2017-06-14 08:27:50
【问题描述】:

我创建了两个java.util.UUID 实例,如下所示。一个是从UUID.randomUUID() 创建的,另一个是相同的,但在开头添加了一些额外的数字。当使用UUID.equals 方法比较它们时,它返回true

UUID uuid1 = UUID.randomUUID();
UUID uuid2 = UUID.fromString("12345"+uuid1.toString());
System.out.println(uuid1.equals(uuid2));  // this gives true.

我认为添加的数字被丢弃并且都给出相同的 UUID 字符串值。为什么会这样?

【问题讨论】:

  • UUID 代表一个 128 位的值。肯定 "12345"+uuid1.toString() 超过了该容量,并在内部被 UUID.fromString(..) 方法截断。您可以尝试打印 uuid1.toString() 和 uuid2.toString 并使用较小的 uuid1 进行测试。
  • 查看 `UUID.fromString()' 的源代码,您添加的额外部分包含在 UUID 的第一个组件中(即第一个“-”之前的部分)。然后它被移到左边,还有很多其他的东西,所以原来的字符串被使用了。看看源代码就明白我的意思了
  • 他们为什么相等是可以理解的。真正的问题是为什么未验证 stringUUID.split("-") 的部分大小。但正如有人在这里stackoverflow.com/a/9771512/3020903 所说的那样,这可能是一个设计决策,除了这段代码的实现者之外没有其他人知道。
  • 谢谢@jr593
  • 谢谢@Tarmo

标签: java uuid


【解决方案1】:

在 UUID.fromString(...) 方法中你有这个:

public static UUID fromString(String name) {
    String[] components = name.split("-");
    if (components.length != 5)
        throw new IllegalArgumentException("Invalid UUID string: "+name);
    for (int i=0; i<5; i++)
        components[i] = "0x"+components[i];

    long mostSigBits = Long.decode(components[0]).longValue();
    mostSigBits <<= 16;
    mostSigBits |= Long.decode(components[1]).longValue();
    mostSigBits <<= 16;
    mostSigBits |= Long.decode(components[2]).longValue();

    long leastSigBits = Long.decode(components[3]).longValue();
    leastSigBits <<= 48;
    leastSigBits |= Long.decode(components[4]).longValue();

    return new UUID(mostSigBits, leastSigBits);
}

它首先将 UUID 的各个部分分成组件,然后它会创建两个 long。在这里,您通过添加前导字符仅更改了 UUID 的第一个组件,您的 UUID 字符串例如是“12345894ff97a-039b-47fe-8a72-950b7766d50c”。所以第一个组件是“12345894ff97a”,什么时候会做

long mostSigBits = Long.decode(components[0]).longValue();

然后你会得到“320256540146042”,它的实际十六进制表示是“12345894ff97a”,然后你有一个位操作会将长 16 位向左移动:

mostSigBits <<= 16;

这将导致“2541588541301456896”实际十六进制表示为“2345894ff97a0000”(我们移动了 16 位,因此 4 个十六进制字符),您开始看到发生了什么,看到长格式是只有 64 位,所以在移位时会丢失一些位,这里第一个字符“1”会丢失。之后在释放空间中添加第二个组件:

mostSigBits |= Long.decode(components[1]).longValue();

这将导致“2541588541301457819”实际十六进制表示为“2345894ff97a039b”,然后它将再次将长 16 位向左移动:

mostSigBits <<= 16;

这将导致“-8552342864911466496”实际十六进制表示为“894ff97a039b0000”,由于长 64 位大小,您的前导字符都丢失了。

剩下的方法我就不用解释了(最后mostSigBits是“894ff97a039b47fe”,leastSigBits是“8a72950b7766d50c”),你已经可以理解任何添加到UUID的前导字符在调用UUID时都会丢失.fromString(..) 因为只考虑最后 8 个十六进制字符(32 位),其余的将丢失。

【讨论】:

    【解决方案2】:

    因为您没有创建两个不同的 UUID。

    UUID.fromString("12345"+uuid1.toString()); 没有意义:UUID.fromString(String) 要求传递的字符串遵守UUID.toString() 中所述的“字符串标准表示”。请参阅UUID.fromString()UUID.toString()

    我想知道为什么你不会得到异常,但我猜你的前几个字符被忽略了。

    【讨论】:

    • “那行不通”是错误的陈述。确实如此。你不是在回答这个问题,你只是从不同的角度重新问这个问题:“我想知道为什么你不会得到例外......”
    • @Tarmo 我想说的是:以这种方式构造一个新的 UUID 是行不通的(关于 UUID 的工作方式也没什么意义)。而且我不同意我只是说我想知道为什么没有例外,因为fromString() 的输入显然无效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-26
    • 1970-01-01
    相关资源
    最近更新 更多