简体   繁体   中英

Comparator for sorting by frequency without Creating a Comparator implementation class

Just curious to know on whether we can sort a List according to the frequency of repeated numbers using Java 8 without writing a Custom Comparator class.

I need to sort the given integers based on its frequency and then by the natural numerical order.

I'm getting error at Comparator.naturalOrder();

Here is the code which I tried:

Integer[] given = new Integer[]{0,0,1,22,11,22,22,11,44,555,55,66,77,88,99};
List<Integer> intList = Arrays.asList(given);


Map<Integer, Long> frequencyMap = intList.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
List<Integer> newList = intList.stream().sorted(Comparator.comparing(frequencyMap::get).thenComparing(Comparator.naturalOrder())).collect(Collectors.toList());
System.out.println(newList.toString());

The expected output is

[1, 44, 55, 66, 77, 88, 99, 555, 0, 0, 11, 11, 22, 22, 22]

PS: Used arrays in first line in order to avoid list.add() in multiple lines and for clear understanding.

Unfortunately, Java's type inference can't recognize the type of the compared object when chaining Comparator.comparing(frequencyMap::get) with thenComparing(Comparator.naturalOrder()) . Since the method signature of Map.get is get(Object) , the compiler infers Comparator<Object> as result type of Comparator.comparing(frequencyMap::get) .

You can fix this by inserting an explicit type. But note that you are not using the result of collect(Collectors.toList()) but just printing the original, unaffected List . On the other hand, you don't need the List when the array is given:

Integer[] given = {0,0,1,22,11,22,22,11,44,555,55,66,77,88,99};

Map<Integer, Long> frequencyMap = Arrays.stream(given)
    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
Arrays.sort(given,
    Comparator.<Integer>comparingLong(frequencyMap::get)
       .thenComparing(Comparator.naturalOrder()));

System.out.println(Arrays.toString(given));

For printing without changing the array you can also use the following alternative

Arrays.stream(given)
    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
    .entrySet().stream()
    .sorted(Map.Entry.<Integer, Long>comparingByValue()
        .thenComparing(Map.Entry.comparingByKey()))
    .flatMap(e -> LongStream.range(0, e.getValue()).mapToObj(l -> e.getKey()))
    .forEach(System.out::println);

This sorts the groups instead of the individual values and prints identical values as often as they've been counted.

you need to add a type witness, minor compiler weakness:

 intList.stream()
        .sorted(Comparator.comparing((Integer x) -> frequencyMap.get(x))
                          .thenComparing(Comparator.naturalOrder()))
        .forEachOrdered(System.out::println);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM