7

I want to sort the following example list which currently contains only Strings with my own custom rules.

ArrayList<String> coll = new ArrayList<>();
coll.add("just");
coll.add("sdsd");
coll.add("asb");
coll.add("b as");
coll.add("just");
coll.add("dhfga");
coll.add("jusht");
coll.add("ktsa");
coll.add("just");
coll.add("just");

I know that I could write my own comparator for this, but as I know that Java also got comparators which solve this problem partially I want to know how I can use the ones from the Java API in combination with my own one.


How should it be sorted?

The word just should always be the first word to appear in the list followed by all other words in alphabetical order.

Comparator.naturalOrder() sorts the list in alphabetical order, but how can I combine this comperator with a custom one which checks whether the word is just or something else.

2
  • Do you want to exclude repeated "just" word or not? Commented May 21, 2018 at 18:56
  • I don't want to exclude them. Commented May 21, 2018 at 18:59

3 Answers 3

11

You can do this something like that:

coll.sort(Comparator
    .comparingInt((String s) -> s.equals("just") ? 0 : 1) // Words "just" first
    .thenComparing(Comparator.naturalOrder())); // Then others
Sign up to request clarification or add additional context in comments.

5 Comments

Boolean is Comparable, so there is no need to deal with int values here: coll.sort(Comparator.comparing((String s) -> !s.equals("just")) .thenComparing(Comparator.naturalOrder()));
@Holger Right. Or even shorter: coll.sort(null); coll.sort(Comparator.comparing("just"::equals).reversed());
Yes, the sort operation is stable and null means “natural order”, but I’d avoid exploiting this; performing two sort operations might not be very efficient and using null as comparator undermines the generic type system, as you won’t have a compile-time check whether the elements are actually comparable. So I’d stay with the compound comparator with a single sort. Or do the “move "just" to front” without sorting like in my answer
@Holger The null part can be fixed with Collections.sort(coll).
Or you stay with coll.sort(Comparator.naturalOrder()));
2

You could integrate the criteria into the comparator like

coll.sort(Comparator.comparing((String s) -> !s.equals("just"))
                    .thenComparing(Comparator.naturalOrder()));

or you separate the operations, first moving all occurrences of "just" to the front, then sorting the remaining elements only:

int howManyJust = 0;
for(int ix = 0, num = coll.size(); ix < num; ix++)
    if(coll.get(ix).equals("just") && ++howManyJust <= ix)
        Collections.swap(coll, ix, howManyJust-1);

coll.subList(howManyJust, coll.size()).sort(Comparator.naturalOrder());

while this may look more complicated, it is potentially more efficient, especially for larger lists.

Comments

0

The first step should be to define the custom order. I would do that by using a Map.

Map<String, Integer> orderMap = new HashMap<>();
int order = 0;
for(String specialWord : yourListOfSpecialWords){
    orderMap.put(specialWord, order++);
}

Now build comparator using that map and natural order as backup:

Comparator<String> comparator = ((Comparator<String>) (o1, o2) -> {
    int leftScore = orderMap.getOrDefault(o1, Integer.MAX_VALUE);
    int rightScore = orderMap.getOrDefault(o2, Integer.MAX_VALUE);
    return Integer.compare(leftScore, rightScore);
}).thenComparing(String::compareTo);

Use this comparator to sort your list. Note: you probably want to initialize your map only once and keep it in a constant or at least in a cache.

But if your special case is only a single word, as your update suggests, then this is of course overkill, and you should go with one of the other answers here.

1 Comment

You can avoid the code duplication by using Comparator.comparingInt((String o) -> orderMap.getOrDefault(o, Integer.MAX_VALUE)). Further, you don’t need the int order variable when you simply use orderMap.put(specialWord, orderMap.size());.

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.