Extrinsic vs intrinsic equality
Note: 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
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
Date objects. If this is not the case, or if objects cannot be compared to one another (because perhapsthey belong to incompatible type as both
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));
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
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.
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.
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:
- It probably is the first argument to use Trove
- I never tested that in any context
To go further, just have a look at Trove for yourself.