Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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".

like image 409
nimo23 Avatar asked Aug 29 '17 14:08

nimo23


3 Answers

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>.

like image 106
Eugene Avatar answered Oct 03 '22 20:10

Eugene


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.

like image 43
Holger Avatar answered Oct 03 '22 19:10

Holger


Use comparing using key extractor parameter:

Comparator<Item> comparator = 
    Comparator.comparing(Item::isOpen, Boolean::compare).reversed();
like image 30
alex.b Avatar answered Oct 03 '22 21:10

alex.b