3

Some List is here

List<Book> list = new ArrayList<>();
{
   list.add(new Book("Core Java", 200));
   list.add(new Book("Core Java", 500));
   list.add(new Book("Core Java", 800));
   list.add(new Book("Learning Freemarker", 150));          
   list.add(new Book("Learning Freemarker", 1350));   
   list.add(new Book("Learning Freemarker", 1250));   
   list.add(new Book("Spring MVC", 300));
   list.add(new Book("Spring MVC", 600)); 
   list.add(new Book("Spring MVC", 1600));
}

I want show Book list like this

Core Java", 800
Learning Freemarker", 1350
Spring MVC", 1600

each 1element

list .stream().distinct()
     .sorted(Comparator.comparing(Book::bookname)
     .thenComparing(Book::getPrice)).collect(Collectors.toList());

this code only sorted.

Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
jjosjoahu
  • 131
  • 7
  • 3
    Please show the code of the `Book` class. Do you have `equals` and `hashCode` overridden in it? – ernest_k Feb 06 '20 at 15:23
  • Book is sample Class `private string bookname ; private string price;` it is done; – jjosjoahu Feb 06 '20 at 16:11
  • 1
    @user3066285 - What @ernest_k is primarily interested in is the `equals` and `hashcode` implementation if you've overridden them because of the use of `distinct` in your sample code. – Naman Feb 06 '20 at 16:14

3 Answers3

6

First you can do a group by on Book name and collect them into Map<String, List<Book>>, And then from map.values() collect the highest price book from each type

List<Book> books = list.stream()
                       .collect(Collectors.groupingBy(Book::getName))
                       .values()
                       .stream()
                       .map(book -> Collections.max(book, Comparator.comparingInt(Book::getCost)))
                       .collect(Collectors.toList());

The other solution suggested by @Holger using Collectors.toMap will be more effective comparing to collecting and finding the max element

List<Book> books = list.stream()
            .collect(Collectors.collectingAndThen(
                    Collectors.toMap(Book::getName, Function.identity(),
                            BinaryOperator.maxBy(Comparator.comparingInt(Book::getCost))),
                    m -> new ArrayList<>(m.values())));
Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
  • 1
    Thank you. you save my time. – jjosjoahu Feb 06 '20 at 15:36
  • 2
    One could have used `grouping` with `reduce` as well to perform the comparison in one collect operation and then further get the values out of it. As I can recall and relate, it's still better to use `toMap` instead of such a grouping and reduce combination. Linking the [Q&A](https://stackoverflow.com/questions/57041896/java-streams-replacing-groupingby-and-reducing-by-tomap) again. – Naman Feb 06 '20 at 16:11
  • 2
    In short, `List books = list.stream() .collect(Collectors.collectingAndThen( Collectors.toMap(Book::getName, Function.identity(), BinaryOperator.maxBy( Comparator.comparing(Book::getPrice))), m -> new ArrayList<>(m.values())));` – Holger Feb 06 '20 at 17:55
  • Thanks @Holger i was trying something similar to this but you got it – Ryuzaki L Feb 06 '20 at 18:05
2

The distinct and then using sorting implies that all distinct books (not just by name) would be selected, further sorting then would still not remove the other entries in the input list until you reduce them somehow. Further based on the desired output, that shouldn't be the way to go.

Instead what you can seek for is using bookName as an attribute to check for uniqueness and then merging the books comparing their prices and collecting them to our final result.

A fairly simple code to what you're looking forwards to would be using toMap such as:

List<Book> distinctMaxPriceBooks = new ArrayList<>(list.stream()
        .collect(Collectors.toMap(Book::getBookName, Function.identity(),
                (b1, b2) -> b1.getPrice() >= b2.getPrice() ? b1 : b2))
        .values());

Of course, these can be sorted based on the name if that was anyhow the requirement.

Naman
  • 27,789
  • 26
  • 218
  • 353
0

One version that makes use of the max method and prints sorted results.

 list.stream()
        .collect(Collectors.groupingBy(Book::getaString))
        .values()
        .stream()
            .map( (books) -> books.stream().max(Comparator.comparingInt(Book::getaNumber)).get())
            .sorted(Comparator.comparingInt(Book::getaNumber))
            .forEach(System.out::println);
Norbert Bicsi
  • 1,562
  • 2
  • 19
  • 33