1

Imagine that I have a domain object with an enum named SortBy to facilitate sorting of the data from the service and / or dao layer.

public class Item {
    public static enum SortBy { CATEGORY, NORWEGIAN, ENGLISH; }

    private String category;
    private String norwegian;
    private String English;
}

This sort of operation is therefore possible: itemDao.getItems(Item.SortBy.CATEGORY)

Now, is there a design pattern or nice way to relate the SortBy values to java Comparator's?

I am asking this since the ways I initially thought about used static, and that's bad practise (right?).

  1. A Map<SortBy, Comparator<Item> in the Item class?

  2. A class ItemComparators with method getComparator(Item.SortBy sort)?

  3. A method on the SortBy enum itself that returns a static instance of Comparator<Item>?

I'm looking for a minimalist way to do this, without going so far as to have a ItemComparatorProvider bean.

Appreciate any information...

3 Answers 3

4

The most straightforward way to do this is to simply have the enum itself implement Comparator:

public enum SortBy implements Comparator<Item> {
    CATEGORY {
        @Override
        public final int compare(final Item o1, final Item o2) {
            return compareStrings(o1.getCategory(), o2.getCategory());
        }
    },
    ENGLISH {
        @Override
        public final int compare(final Item o1, final Item o2) {
            return compareStrings(o1.getEnglish(), o2.getEnglish());
        }
    },
    NORWEGIAN {
        @Override
        public final int compare(final Item o1, final Item o2) {
            return compareStrings(o1.getNorwegian(), o2.getNorwegian());
        }
    };

    private static int compareStrings(final String s1, final String s2) {
        if (s1 == null) {
            return s2 == null ? 0 : -1;
        }
        if (s2 == null) {
            return 1;
        }
        return s1.compareTo(s2);
    }
}

There's no need to further associate each enum member with a comparator because the enum members are the comparators. The usage is then nice and concise:

Collections.sort(items, SortBy.CATEGORY);

Addendum

In the comments below, you asked about common, null-safe comparisons. I'm not sure what third-party libraries might provide them, but you can implement them yourself easily enough. Here's two from one of our internal libraries:

static <T extends Comparable<? super T>> int compare(final T o1, final T o2) {
    if (o1 == null) {
        return o2 == null ? 0 : -1;
    }
    if (o2 == null) {
        return 1;
    }
    return o1.compareTo(o2);
}

@SuppressWarnings("unchecked")
static int compare(final Object a, final Object b) {
    if (a == b) {
        return 0;
    }

    if (a == null) {
        return -1;
    }

    if (b == null) {
        return 1;
    }

    final Class<?> aClass = a.getClass();
    final Class<?> bClass = b.getClass();

    if (Comparable.class.isInstance(a) && aClass.isAssignableFrom(bClass)) {
        return ((Comparable<Object>)a).compareTo(b);
    }

    if (Comparable.class.isInstance(b) && bClass.isAssignableFrom(aClass)) {
        return ((Comparable<Object>)b).compareTo(a);
    }

    throw new IllegalArgumentException("Values must be comparable.");
}
Sign up to request clarification or add additional context in comments.

5 Comments

Nice, just what I was after. Thank you.
Glad to help! I wasn't 100% sure it was possible until I tried it just now, so I learned something too :).
I also see you put in a null-safe string compare, so I learnt something too. Is that also availabel in commons or some place like that?
I'm not sure. We don't use a lot of third-party (or even J2SE) code here. I posted a couple examples from our own Compare class.
I see also that commons lang3 ObjectUtils.compare() can also be used - it looks equivalent to your code above, at a quick glance.
2

You can think about something like this:

public static enum SortBy {

 CATEGORY(new CategoryComparator()), NORWEGIAN(new NorwegianComparator()), ENGLISH(new EnglishComparator());

    Comparator<Item> comparator;

    private SortBy(Comparator<Item> comparator) {
        this.comparator = comparator;
    }

}

You can either implement Comparator and create new classes if you will use them elsewhere or implement them as anonymous inner classes like in vikingsteve's answer.

Comments

1

Ok well here is one way to do it. Is this fine?

public static enum SortBy {
    CATEGORY(new Comparator<Item>() {
        @Override
        public int compare(Item o1, Item o2) {
            return o1.getCategory().compareTo(o2.getCategory());
        }
    }),
    NORWEGIAN(new Comparator<Item>() {
        @Override
        public int compare(Item o1, Item o2) {
            return o1.getNorwegian().compareTo(o2.getNorwegian());
        }
    }),
    ENGLISH(new Comparator<Item>() {
        @Override
        public int compare(Item o1, Item o2) {
            return o1.getEnglish().compareTo(o2.getEnglish());
        }
    });
    private Comparator<Item> comparator;

    private SortBy(Comparator<Item> comparator) {
        this.comparator = comparator;
    }

    public Comparator<Item> getComparator() {
        return comparator;
    }
}

3 Comments

Yes. Though you could make it more concise by using BeanComparator and only passing the property in the constructor.
Where can he find the class BeanComparator?.
I believe it's in one of the Apache Commons libraries. commons-beanutils, maybe?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.