2

I have an input of integers and I would like to sort all even numbers before all odd ones in ascending order and preserve the odds order. I am pretty sure I can achieve that with collectors and/or downstream collectors, but I am not sure how to do it. I would like to know how can I sort the false list, so I can achieve it in one line if possible. So after the grouping by I want to have the false list sorted in ascending and the other one (the true list of odds) to remain the same and after this conversion to be able to flatmap them into one list.

Example:

Input is:

1 6 2 3 4 5
Map<Boolean, List<Integer>> collect = Arrays
        .stream(bf.readLine().split("\\s+"))
        .map(Integer::parseInt)
        .collect(Collectors.groupingBy(integer -> integer % 2 != 0));

System.out.println(collect);

The output of the code above is:

{false=[6, 2, 4], true=[1, 3, 5]}

This line of entries should become:

{false=[2, 4, 6], true=[1, 3, 5]}

after the conversion.

Output should be evens to come before the odds and to be sorted in ascending order and to preserve the order of the odds at the same time, like this:

2 4 6 1 3 5
khelwood
  • 55,782
  • 14
  • 81
  • 108
  • 3
    Do you actually want them in separate collections, or just evens in order, then odds in order? If the latter, `list.sort(Comparator.comparing(i -> i % 2 != 0).thenComparingInt(i -> i))` would suffice. – Andy Turner Apr 30 '21 at 12:47

3 Answers3

1

Use the overload of groupingBy which takes a downstream collector

For example:

groupingBy(
    integer -> integer % 2 != 0,
    collectingAndThen(
        toList(),
        list -> list.stream().sorted().collect(toList()))
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • Thanks, but this does not preserve the order of the odds. It sorts all of the lists. I would like to sort only the false list in my case. 1 2 3 4 5 6 3 {false=[2, 4, 6], true=[1, 3, 5, 3]}, but I get 1 2 3 4 5 6 3 {false=[2, 4, 6], true=[1, 3, 3, 5]} – unknownerror Apr 30 '21 at 12:51
  • @unknownerror [works fine here](https://ideone.com/nGU2qS). – Andy Turner Apr 30 '21 at 13:16
  • 1 2 3 4 5 6 3 should sort evens -> 2 4 6 and preserve the order of the odds 1 3 5 3, it is a bit funky – unknownerror Apr 30 '21 at 13:21
  • 1
    `list -> (list.get(0) % 2 != 0) ? list : list.stream().sorted().collect(toList())`. – Andy Turner Apr 30 '21 at 13:36
1

You don't actually need to group by. The Stream.sorted method is a stable sort if the stream is ordered.

You just need a comparator such as:

Comparator.comparingInt(x -> x % 2)
    // if it is even, order by natural order, otherwise they are "equal", so we map to a constant
    .thenComparingInt(x -> x % 2 == 0 ? x : 0)

Example:

Stream.of(3, 6, 2, 1, 4, 5).sorted(
    Comparator.<Integer>comparingInt(x -> x % 2)
        .thenComparingInt(x -> x % 2 == 0 ? x : 0))
    .forEachOrdered(System.out::println);

Prints:

2
4
6
3
1
5

If you want a Map<Boolean, List<Integer>> instead, you can partition by the parity (this will guarantee that the false key always exist in the map), and then sort the list associated with false:

Map<Boolean, List<Integer>> partitions = Stream.of(3, 6, 2, 1, 4, 5).collect(
    // collects to ArrayList to guarantee that it is mutable
    Collectors.partitioningBy(x -> x % 2 != 0, Collectors.toCollection(ArrayList::new))
);
partitions.get(false).sort(Comparator.naturalOrder());
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Thanks, works like I expected. The given syntax Comparator.comparingInt is a bit weird to me considering the fact that we have stream of ints and if i do Comparator.comparingInt(my logic) it is complaining. – unknownerror Apr 30 '21 at 13:15
  • 1
    @unknownerror yeah, it somehow can't infer the generic types, so I had to specify it explicitly. You can declare a local variable of type `Comparator` first, and pass that to `sorted`, if you don't like the weird syntax. – Sweeper Apr 30 '21 at 13:18
1

For the sole fun of solving a simple problem in a complicated way using Collectors.teeing available in Java 12 and higher:

List<Integer> list = List.of(3, 6, 2, 1, 4, 5);

List<Integer> result = 
    list.stream()
        .collect(Collectors.teeing(
                    Collectors.filtering(i -> i % 2 == 0, Collectors.toList()), 
                    Collectors.filtering(i -> i % 2 != 0, Collectors.toList()), 
                    (List<Integer> evens, List<Integer> odds) -> {
                        List<Integer> merged = new ArrayList<>();
                        evens.sort(Comparator.naturalOrder());
                        merged.addAll(evens);
                        merged.addAll(odds);
                        return merged;
                    }
        ));

System.out.println(result);
Eritrean
  • 15,851
  • 3
  • 22
  • 28