Home > other >  How to use partitioningBy and then sort resulting lists separately using Java Streams
How to use partitioningBy and then sort resulting lists separately using Java Streams

Time:06-10

I have an object like following:

public class Resource {
  int level;
  String identifier;
  boolean isEducational;

  public Resource(String level, String identifier, boolean isEducational) {
            this.level = level;
            this.identifier = identifier;
            this.isEducational = isEducational;
        }

 // getter and setters 
}

And a list of these Resources like:

List<Resource> resources = Arrays.asList(new Resource(4, "a", true ),
                                                new Resource(4, "b", false),
                                                new Resource(3, "c", true ),
                                                new Resource(3, "d", false ),
                                                new Resource(2, "e", true ),
                                                new Resource(2, "f" , false));

I want to sort this list by their level property but this sorting should be done separately for isEducational resources and non-isEducational resources.

So, after sorting, the resultant list should be in the following order:

[Resource e, Resource c, Resource a, Resource f, Resource d, Resource b]

// basically, isEducational sorted first, followed by non-educational resources

So I tried following:

List<Resource> resources1 = resources.stream()
                .collect(partitioningBy(r -> r.isEducational()))
                .values()
                .stream()
                .map(list -> {
                    return list
                            .stream()
                            .sorted(comparing(r -> r.getLevel()))
                            .collect(toList());
                })
                .flatMap(Collection::stream)
                .collect(toList());


resources1.stream().forEach(System.out::println);

And it prints the output as:

Resource{level='2', identifier='f', isEducational='false'}
Resource{level='3', identifier='d', isEducational='false'}
Resource{level='4', identifier='b', isEducational='false'}
Resource{level='2', identifier='e', isEducational='true'}
Resource{level='3', identifier='c', isEducational='true'}
Resource{level='4', identifier='a', isEducational='true'}

Which is opposite of what I want i.e. its printing non-educational first, followed by educational resources

Is there a better way to achieve this ? I do not want to iterate the list again to rearrange it. Thanks.

CodePudding user response:

No need to using partitioningBy at all. You just need two comparators to first to compare by isEducational and then by level, which you can chain by using Comparator.thenComparing

resources.stream()
         .sorted(Comparator.comparing(Resource::isEducational).reversed().thenComparing(Resource::getLevel))
         .forEach(System.out::println);

You can introduce variables for the comparators to make your code more readable or if you want to reuse them in a flexible manner:

Comparator<Resource> byIsEdu = Comparator.comparing(Resource::isEducational).reversed();
Comparator<Resource> byLevel = Comparator.comparing(Resource::getLevel);

resources.stream()
         .sorted(byIsEdu.thenComparing(byLevel))
         .forEach(System.out::println);
  • Related