不同字符串生成的UUID相同

46

我有两个不同的字符串,但在转换为 UUID 后它们似乎相同。

public static void main(String[] args) {
    try {
        UUID t1 = UUID.fromString("38e1036d-7527-42a3-98ca-f2f19d3155db");
        UUID t2 = UUID.fromString("123438e1036d-7527-42a3-98ca-f2f19d3155db");
        System.out.println(t1.toString().equals(t2.toString()));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

你知道为什么会这样吗?


6
你得到了什么输出?你期望得到什么输出?如果打印这两个结果而不是比较它们,会发生什么? - David Schwartz
6
也许是因为 UUID(通用唯一标识符)除了开头的“1234”之外,实际上完全相同?如果改变 UUID 的其余部分而不是在开头添加 4 位数字会发生什么呢? - n247s
6
听起来像是一个 Bug。应该抛出某种异常告诉你它不是一个有效的 UUID。 - user253751
5个回答

70

"123438e1036d-7527-42a3-98ca-f2f19d3155db"

这不是一个UUID,而是由"1234"和一个UUID拼接而成的字符串。问题在于解析器本应该通过抛出异常来告诉你这一点,而不是尝试从中寻找嵌藏的UUID。

一旦你从拼接的字符串中提取出UUID,它就与第一个UUID完全相同,这才是你所观察到的正确结果。

我们可以分析解析器(感谢@tim-biegeleisen提供的链接):

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);
}

正如我们所看到的,除了计算由连字符限制的组数外,没有任何验证。它只是获取这些组,然后将它们移动到相应位置。您在第一组之前添加了额外的字符,这是最重要的部分。首先对其进行解析和存储,然后将其上移并再次上移,直到它占据最重要的部分。现在,所有比预期更靠左的位都被推出long限制,因此它们被完全忽略。


1
@Raedwald 嗯,验证默认存在更多是惯例问题。我不太了解Java,无法说出惯例是什么。 - Agent_L

53

UUID 存储 128 位数据。如果提供的数据超出了 128 位,它将无法存储这些数据。我很惊讶它没有给出错误提示,但不意外它会截断高位数据。

int i = 0x38e1036d;
int j = (int) 0x123438e1036dL;
i == j;

8
若想查看源代码的实际操作,请访问以下链接:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/UUID.java#UUID.fromString%28java.lang.String%29 - Tim Biegeleisen
1
mostSigBitsleastSigBits 超过64位时,它会被截断。 - Viet
1
我会为此提交一个错误报告。我最好的猜测是,他们这样做的原因可能是希望允许UUID的每个部分更短,以便不需要前导零?我不确定。 - jpmc26

9
第二个组件'7527'的位移消除了您对第一个组件'123438e1036d'所做修改的影响,导致生成相同的UUID。
单独处理第一个组件的过程是不同的,但当uuid的第二个组件发生位移时,这种效果就会丢失。

4

4

这是因为它从右到左检查,只取32个字符作为UUID,并删除其他字符。一旦按顺序取了32个字符,它就不再关心其他字符,因为它实现了Serializable接口。

public final class UUID
  extends Object
  implements Serializable, Comparable<UUID>

你的第二个UUID中去掉了1234。

以下是它的代码,可以更好地帮助你:

 public static UUID More ...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);
    }

1
如果它是“从左到右”并且“仅占用32个字符”,那么55db将被忽略。但它不是... - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接