简体   繁体   中英

java 8 stream collect max object and distinct by Property

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.

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())));

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.

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);

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