Wednesday, May 03, 2017

A toString Comparator for arbitrary objects

I recently ran into a problem where I wanted to add objects attained by reflection to a TreeSet. In this specific case, I had a problem when a Locale object was retrieved. It could not be added to the TreeSet because it is not "Comparable." It did, however, have toString values that could be sorted intelligibly.

I decided to create a Comparator that would handle arbitrary objects, and allow them to be added to a TreeSet of type Object:

package com.threeleaf.util;
import java.util.Comparator;
/**
* A {@link Comparator} that allows any two objects to be sorted based on {@link Object#toString()}.
* This works best when the object's toString returns something meaningful, but can still be
* useful in cases where a object one has not control over needs to be placed in a sorted
* collection.
* <p>
* Usage:
* <pre>{@code
* final Set<Object> set = new TreeSet<>(new ToStringComparator());
* // en_US
* set.add(Locale.US);
* // en_CA
* set.add(Locale.CANADA);
* // en
* set.add(Locale.ENGLISH);
* }</pre>
* produces the sorted set: {@code [ENGLISH, CANADA, US]} instead of an exception.
*/
@SuppressWarnings("WeakerAccess")
public class ToStringComparator implements Comparator<Object>
{
/** Instantiate a new {@link ToStringComparator}. */
public ToStringComparator()
{
super();
}
/** {@inheritDoc}. */
public int compare(final Object object1, final Object object2)
{
return object1.toString().compareTo(object2.toString());
}
}

The caveat is that this works best when the object in question overrides the default toString method in a meaningful way. If the base Object.toString() method is inherited by the object, then the sorting will most likely appear random, since the method looks like this:

    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

No comments: