Extrinsic vs intrinsic equality

The following article is purely theoretical. I don’t know if it fits a real-life use-case, but the point is just too good to miss :-)

Java’s List sorting has two flavors: one follows the natural ordering of collection objects, the other requires an external comparator.

In the first case, Java assumes objects are naturally ordered. From a code point of view, this means types of objects in the list must implement the Comparable interface. For example, such is the case for String and Date objects. If this is not the case, or if objects cannot be compared to one another (because perhaps they belong to incompatible type as both String and Date).

The second case happens when the natural order is not relevant and a comparator has to be implemented. For example, strings are sorted according to the character value, meaning case is relevant. When the use-case requires a case-insensitive sort, the following code will do (using Java 8 enhanced syntax):

Collections.sort(strings, (s1, s2) -> s1.compareToIgnoreCase(s2));

The Comparable approach is intrinsic, the Comparator extrinsic; the former case rigid, the latter adaptable to the required context.

What applies to lists, however, cannot be applied to Java sets. Objects added to sets have to define equals() and hashCode() and both properties (one could say that it’s only one since they are so coupled together) are intrinsic. There is no way to define an equality that can change depending on the context in the JDK.

Enters Trove:

The Trove library provide primitive collections with similar APIs to the above. This gap in the JDK is often addressed by using the "wrapper" classes (java.lang.Integer, java.lang.Float, etc.) with Object-based collections. For most applications, however, collections which store primitives directly will require less space and yield significant performance gains.

Let’s be frank, Trove is under-documented. However, it offers what is missing regarding extrinsic equality: it provides a dedicated set implementation, that accepts its own extrinsic equality abstraction.

Trove class diagram

A sample code would look like that:

HashingStrategy<Date> strategy = new MyCustomStrategy();
Set<Date> dates = new TCustomHashSet<Date>(strategy);

A big bonus for using Trove is performance, though:

  1. It probably is the first argument to use Trove
  2. I never tested that in any context

To go further, just have a look at Trove for yourself.

Nicolas Fränkel

Nicolas Fränkel

Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Also double as a trainer and triples as a book author.

Read More
Extrinsic vs intrinsic equality
Share this