1

I have

Map<String,LongAdder>

and I want to sort by the value the best way in streams. This is harder than with Long because LongAdder does not implement Comparable so I have to use longValue (or intValue if using subtraction to make the Comparator).

I know I can use

m.entrySet().stream().sorted((a, b) -> b.getValue().intValue() - a.getValue().intValue())

but I actually want to also sort secondarily on the key (String). I'm also reversing the sort.

I want to do

m.entrySet().stream().sorted(
Comparator.comparing((a, b) -> b.getValue().intValue() - a.getValue().intValue()))

so that I can chain more comparators afterward with thenComparing()

The exception is

Lambda expression's signature does not match the signature of the functional interface method apply(T)

But even declaring a standalone Comparator this does not work:

Comparator<Map.Entry<String,LongAdder>> byCount =      Comparator.comparing((a,b) -> 
    (b.getValue().intValue() - a.getValue().intValue()));

Lambda expression's signature does not match the signature of the functional interface method apply(T)

I can't use functional reference "::" because it's too many parts: Map.Entry.getValue().intValue().

Naman
  • 27,789
  • 26
  • 218
  • 353
dhenry
  • 13
  • 2
  • 3
    `Comparator.comparing` does not take a `Comparator` - it takes a function that maps from your object to a comparable. Also, never use subtraction to compare - it can overflow and give you the wrong results. Use `Integer.compare`. Also, don't use `intValue` if your numbers are actually longs - again, you lose information. – RealSkeptic Jun 17 '19 at 14:19

2 Answers2

2

Comparator.comparingLong is what you need:

Comparator<Map.Entry<String, LongAdder>> byCount =
    Comparator.comparingLong((Map.Entry<String, LongAdder> e) ->
        e.getValue().longValue()).reversed().thenComparing(...);

Important note: don't forget to use an explicitly typed lambda expression, otherwise method chaining like comparing(...).thenComparing(...) won't compile in this particular case1.


1 - This answer explains why.

Oleksandr Pyrohov
  • 14,685
  • 6
  • 61
  • 90
  • 1
    For one level, you can avoid the explicit types when using `Comparator> byCount = Map.Entry.comparingByValue(Comparator.comparingLong(LongAdder::sum).reversed( ));` But when chaining another sort criteria, you end up needing explicit types, like `Comparator> byCount2 = Map.Entry.comparingByValue(Comparator. comparingLong(LongAdder::sum).reversed()).thenComparing(…);` – Holger Jun 18 '19 at 16:29
1

Your standalone comparator could be fixed to use a Function and would look like :

Comparator<Map.Entry<String,LongAdder>> byCount = Comparator.comparingInt(e -> e.getValue().intValue());
Naman
  • 27,789
  • 26
  • 218
  • 353