nimo23
nimo23

Reputation: 5698

Comparator.compareBoolean() the same as Comparator.compare()?

How can I write this

Comparator <Item> sort = (i1, i2) -> Boolean.compare(i2.isOpen(), i1.isOpen());

to something like this (code does not work):

Comparator<Item> sort = Comparator.comparing(Item::isOpen).reversed();

Comparing method does not have something like Comparator.comparingBool(). Comparator.comparing returns int and not "Item".

Upvotes: 10

Views: 15857

Answers (3)

Holger
Holger

Reputation: 298389

The overloads comparingInt, comparingLong, and comparingDouble exist for performance reasons only. They are semantically identical to the unspecialized comparing method, so using comparing instead of comparingXXX has the same outcome, but might having boxing overhead, but the actual implications depend on the particular execution environment.

In case of boolean values, we can predict that the overhead will be negligible, as the method Boolean.valueOf will always return either Boolean.TRUE or Boolean.FALSE and never create new instances, so even if a particular JVM fails to inline the entire code, it does not depend on the presence of Escape Analysis in the optimizer.

As you already figured out, reversing a comparator is implemented by swapping the argument internally, just like you did manually in your lambda expression.

Note that it is still possible to create a comparator fusing the reversal and an unboxed comparison without having to repeat the isOpen() expression:

Comparator<Item> sort = Comparator.comparingInt(i -> i.isOpen()? 0: 1);

but, as said, it’s unlikely to have a significantly higher performance than the Comparator.comparing(Item::isOpen).reversed() approach.


But note that if you have a boolean sort criteria and care for the maximum performance, you may consider replacing the general-purpose sort algorithm with a bucket sort variant. E.g.

If you have a Stream, replace

List<Item> result = /* stream of Item */
    .sorted(Comparator.comparing(Item::isOpen).reversed())
    .collect(Collectors.toList());

with

Map<Boolean,List<Item>> map = /* stream of Item */
    .collect(Collectors.partitioningBy(Item::isOpen,
                                       Collectors.toCollection(ArrayList::new)));
List<Item> result = map.get(true);
result.addAll(map.get(false));

or, if you have a List, replace

list.sort(Comparator.comparing(Item::isOpen).reversed());

with

ArrayList<Item> temp = new ArrayList<>(list.size());
list.removeIf(item -> !item.isOpen() && temp.add(item));
list.addAll(temp);

etc.

Upvotes: 6

alex.b
alex.b

Reputation: 1528

Use comparing using key extractor parameter:

Comparator<Item> comparator = 
    Comparator.comparing(Item::isOpen, Boolean::compare).reversed();

Upvotes: 2

Eugene
Eugene

Reputation: 120968

Why can't you write it like this?

 Comparator<Item> sort = Comparator.comparing(Item::isOpen);

Underneath Boolean.compareTo is called, which in turn is the same as Boolean.compare

public static int compare(boolean x, boolean y) {
    return (x == y) ? 0 : (x ? 1 : -1);
}

And this: Comparator.comparing returns int and not "Item". make little sense, Comparator.comparing must return a Comparator<T>; in your case it correctly returns a Comparator<Item>.

Upvotes: 9

Related Questions