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:
@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:
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:
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
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
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.