Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

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 of 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 article, we will have a look at the UUID class in Java.  First, we'll look at how to use the class itself. Then, we'll look at the different types of UUIDs and how we can generate them in Java.

Further reading:

CharSequence vs. String in Java

Learn the differences between CharSequence and String.

Use char[] Array Over a String for Manipulating Passwords in Java?

Explore several reasons why we shouldn't use Strings for storing passwords and use char[] arrays instead.

Guide to Java String Pool

Learn how the JVM optimizes the amount of memory allocated to String storage in the Java String Pool.

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. These are:

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

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

UUID uuid = UUID.randomUUID();

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

UUID uuid = UUID.fromString(String uuidHexDigitString);

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

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 which determines the layout of the UUID. All other bits in the UUID depends on the setting of the bits in the variant field. The variant is determined by 3 most significant bit 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 5 different versions for variant 2 UUIDs: Time Based (UUIDv1), DCE Security (UUIDv2), Name Based (UUIDv3 and UUIDv5), 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 the 15th of October 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 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, therefore, we won't look at an implementation in this article.

4.3. Version 3 & 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.

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

4.4. Version 4

The UUID v4 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, which variants and versions there are. We 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.

Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Comments are closed on this article!