Skip to content

UUID Comparison in Java: RFC 4122 Compliance and Workarounds

The Java standard library's implementation of UUID comparison violates the RFC 4122 specification due to its use of signed rather than unsigned integer comparison. This article explains the discrepancy and provides solutions for compliant UUID comparison.

The RFC 4122 Standard Requirements

According to RFC 4122, UUIDs should be treated as unsigned 128-bit integers and compared using lexical equivalence rules:

Consider each field of the UUID to be an unsigned integer... Then, to compare a pair of UUIDs, arithmetically compare the corresponding fields from each UUID in order of significance and according to their data type.

The standard also specifies lexicographical ordering:

The first UUID follows the second if the most significant field in which the UUIDs differ is greater for the first UUID.

The Java Implementation Issue

Java's UUID.compareTo() method implementation uses signed comparison:

java
@Override
public int compareTo(UUID val) {
    // The ordering is intentionally set up so that the UUIDs
    // can simply be numerically compared as two numbers
    int mostSigBits = Long.compare(this.mostSigBits, val.mostSigBits);
    return mostSigBits != 0 ? mostSigBits : Long.compare(this.leastSigBits, val.leastSigBits);
}

This causes problems because UUIDs contain values that exceed Long.MAX_VALUE, leading to incorrect comparisons when negative values are interpreted as signed integers.

For example, comparing:

java
UUID.fromString("b533260f-6479-4014-a007-818481bd98c6")
UUID.fromString("131f0ada-6b6a-4e75-a6a0-4149958664e3")

Returns true (indicating the first is "less than" the second) when it should return false according to RFC 4122.

Official Java Position

This behavior has been reported as JDK-7025832 - "java.lang.UUID compareTo() does not do an unsigned compare." However, it has been marked as "won't fix" to maintain backward compatibility across Java versions.

WARNING

Changing the comparison behavior would break existing applications that depend on the current implementation, which is why Oracle has chosen not to fix this issue.

RFC-Compliant Comparison Solution

To compare UUIDs according to RFC 4122 standards, implement a custom comparator using unsigned comparison:

java
Comparator<UUID> rfcComparator = (a, b) -> {
    int mostSigBits = Long.compareUnsigned(a.getMostSignificantBits(), 
                                          b.getMostSignificantBits());
    return mostSigBits != 0 ? mostSigBits : 
           Long.compareUnsigned(a.getLeastSignificantBits(), 
                               b.getLeastSignificantBits());
};

Practical Usage Examples

Basic UUID Comparison

java
UUID uuid1 = UUID.fromString("b533260f-6479-4014-a007-818481bd98c6");
UUID uuid2 = UUID.fromString("131f0ada-6b6a-4e75-a6a0-4149958664e3");

// Standard Java comparison (non-RFC compliant)
boolean standardResult = uuid1.compareTo(uuid2) < 0; // Returns true

// RFC-compliant comparison
boolean rfcResult = rfcComparator.compare(uuid1, uuid2) < 0; // Returns false

Sorting UUID Collections

java
List<UUID> uuidList = Arrays.asList(uuid1, uuid2, uuid3);

// Standard sorting (non-RFC compliant)
Collections.sort(uuidList);

// RFC-compliant sorting
uuidList.sort(rfcComparator);

Recommendation

Use the custom RFC-compliant comparator when:

  • Interoperating with systems that strictly follow RFC 4122
  • Storing UUIDs in databases or systems that use RFC ordering
  • When consistent cross-platform UUID ordering is required

Use Java's built-in compareTo() when:

  • Working exclusively within Java applications
  • Maintaining backward compatibility with existing Java code
  • Performance is critical (built-in may be slightly faster)

INFO

This discrepancy primarily affects UUIDs where the most significant bits have the high bit set (values ≥ 2^63), making them negative when interpreted as signed longs.

Summary

Java's UUID comparison implementation deviates from RFC 4122 for backward compatibility reasons. While the standard library implementation remains unchanged, developers can easily implement RFC-compliant comparison using Long.compareUnsigned() when needed.