Guide to UUID in Java

1. Overview

UUID (Universally Unique Identifier), also known as GUID (Globally Unique Identifier) represents a 128-bit long value that is unique for all practical purposes. The standard representation of the UUID uses hex digits (octets):

123e4567-e89b-12d3-a456-556642440000

A UUID is made up of hex digits  (4 chars each) along with 4 “-” symbols, which make its length equal to 36 characters.

The Nil UUID is a special form of UUID in which all bits are set to zero.

In this tutorial, we will have a look at the UUID class in Java.  First, we’ll see how to use the class itself. Then we’ll look at the different types of UUIDs and how we can generate them in Java.

2. The UUID Class

The UUID class has a single constructor:

UUID uuid = new UUID(long mostSignificant64Bits, long leastSignificant64Bits);

If we want to use this constructor, we need to provide two long values. However, it requires us to construct the bit-pattern for the UUID ourselves.

For convenience, there are three static methods to create a UUID.

This first method creates a version 3 UUID from the given byte array:

UUID uuid = UUID.nameUUIDFromBytes(byte[] bytes);

Second, the randomUUID() method creates a version 4 UUID. This is the most convenient way of creating a UUID:

UUID uuid = UUID.randomUUID();

The third static method returns a UUID object given the string representation of a given UUID:

UUID uuid = UUID.fromString(String uuidHexDigitString);

Let’s now look at how a UUID is structured.

3. Structure

Let’s take the example UUID:

123e4567-e89b-42d3-a456-556642440000
xxxxxxxx-xxxx-Bxxx-Axxx-xxxxxxxxxxxx

3.1. UUID Variant

A represents the variant that determines the layout of the UUID. All other bits in the UUID depend on the setting of the bits in the variant field. The variant is determined by the three most significant bits of A:

  MSB1    MSB2    MSB3
   0       X       X     reserved (0)
   1       0       X     current variant (2)
   1       1       0     reserved for Microsoft (6)
   1       1       1     reserved for future (7)

The value of A in the mentioned UUID is “a”. The binary equivalent of “a” (=10xx) shows the variant as 2.

3.2. UUID Version

B represents the version. The version in the mentioned UUID (value of B) is 4.

Java provides methods for getting variant and version of UUID:

UUID uuid = UUID.randomUUID();
int variant = uuid.variant();
int version = uuid.version();

These are five different versions for variant 2 UUIDs: Time Based (UUIDv1), DCE Security (UUIDv2), Name Based (UUIDv3 and UUIDv5), and Random (UUIDv4).

Java provides an implementation for the v3 and v4 but also provides a constructor for generating any type of UUID:

UUID uuid = new UUID(long mostSigBits, long leastSigBits);

4. The UUID Versions

4.1. Version 1

UUID version 1 is based on the current timestamp, measured in units of 100 nanoseconds from October 15, 1582, concatenated with the MAC address of the device where the UUID is created.

If privacy is a concern, UUID version 1 can alternatively be generated with a random 48-bit number instead of the MAC address. In this article, we’ll look at this alternative.

First, we’ll generate the 64 least and most significant bits as long values:

private static long get64LeastSignificantBitsForVersion1() {
    Random random = new Random();
    long random63BitLong = random.nextLong() & 0x3FFFFFFFFFFFFFFFL;
    long variant3BitFlag = 0x8000000000000000L;
    return random63BitLong + variant3BitFlag;
}

private static long get64MostSignificantBitsForVersion1() {
    LocalDateTime start = LocalDateTime.of(1582, 10, 15, 0, 0, 0);
    Duration duration = Duration.between(start, LocalDateTime.now());
    long seconds = duration.getSeconds();
    long nanos = duration.getNano();
    long timeForUuidIn100Nanos = seconds * 10000000 + nanos * 100;
    long least12SignificatBitOfTime = (timeForUuidIn100Nanos & 0x000000000000FFFFL) >> 4;
    long version = 1 << 12;
    return 
      (timeForUuidIn100Nanos & 0xFFFFFFFFFFFF0000L) + version + least12SignificatBitOfTime;
}

We can then pass these two values to the constructor of the UUID:

public static UUID generateType1UUID() {

    long most64SigBits = get64MostSignificantBitsForVersion1();
    long least64SigBits = get64LeastSignificantBitsForVersion1();

    return new UUID(most64SigBits, least64SigBits);
}

4.2. Version 2

Version 2 is based on a timestamp and the MAC address as well. However, RFC 4122 does not specify the exact generation details, so we won’t look at an implementation in this article.

4.3. Versions 3 and 5

The UUIDs are generated using the hash of namespace and name. The namespace identifiers are UUIDs like Domain Name System (DNS), Object Identifiers (OIDs), URLs, etc.

UUID = hash(NAMESPACE_IDENTIFIER + NAME)

The only difference between UUIDv3 and UUIDv5 is the Hashing Algorithm — v3 uses MD5 (128 bits), while v5 uses SHA-1 (160 bits).

Simply put, we truncate the resulting hash to 128 bits and then replace 4 bit for the version and 2 bit for the variant.

Let’s generate type 3 UUID:

byte[] nameSpaceBytes = bytesFromUUID(namespace);
byte[] nameBytes = name.getBytes("UTF-8");
byte[] result = joinBytes(nameSpaceBytes, nameBytes);

UUID uuid = UUID.nameUUIDFromBytes(result);

Here, it’s important to note that the hex string for the namespace first needs to be converted to a byte array.

Finally, Java doesn’t provide the implementation for type 5. Check our source code repository for the UUIDv5.

4.4. Version 4

The UUIDv4 implementation uses random numbers as the source. The Java implementation is SecureRandom, which uses an unpredictable value as the seed to generate random numbers to reduce the chance of collisions.

Let’s generate version 4 UUID:

UUID uuid = UUID.randomUUID();

Let’s generate a unique key using “SHA-256” and a random UUID:

MessageDigest salt = MessageDigest.getInstance("SHA-256");
salt.update(UUID.randomUUID().toString().getBytes("UTF-8"));
String digest = bytesToHex(salt.digest());

5. Conclusion

In this article, we saw how a UUID is structured and which variants and versions there are.

We also learned for which versions Java provides an out-of-the-box implementation and looked at code examples to generate the other versions.

And as always, the source code of implementation is available over on GitHub.