UUID is same from different strings

I have two different strings, but after parsing to UUID it's seem to be same

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

Any idea why it is?


Solution 1:

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

This is not a UUID. It's a concatenated string of "1234" and a UUID. The problem here is that the parser should have told you this by throwing an exception. Instead it tries its best to actually find the UUID buried somewhere in there.

Once you extract the UUID from your concatenated string, it's identical to the first UUID which is the correct result you're observing.

We can analyze the parser (thanks to @tim-biegeleisen for providing the link):

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

As we can see, there is no validation except for counting the number of groups limited by hyphens. It just takes those groups and then shifts them into positions. You've added extra characters in front of the first group, which is the most significant part. It's parsed and stored first, and then it gets shifted up and again up until it occupies the most significant part. Now, all bits that were farther left than assumed get pushed out of the long limit, hence they're completely ignored.

Solution 2:

A UUID stores 128 bits of data. If you give it more it can't store them. I am surprised it doesn't give you an error, but not surprised it truncates the higher bits otherwise.

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

Solution 3:

The bit shifting of the second component '7527' removes the effect of the modification you made to the first component '123438e1036d' which results in the same UUID being generated.

The processing of the first component by itself is different but this effect is lost when the second component of the uuid is shifted.

Solution 4:

That is because it checks from right to left, and takes only 32 characters as UUID, and removes others. once 32 character in order is taken, it doesnt care about others, as it implements Serializable interface.

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

1234 is trimmed from you 2nd UUID.

Here is its code, it helps much better

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