如何创建List <t>到Map <string,t =“”>而不是Map <string,list <t =“” >>?

时间:2019-02-11 13:51:20

标签: java java-8 java-stream

我遇到了需要将List<Book>转换为Map<String, Book>的情况,而我能找到的唯一解决方案是如何进行Map<String, List<Book>>

类本身看起来是这样的(我省略了getters / setter和构造函数):

public class Book {
    private String asin;
    private String author;
    private String title;
}

我想通过某些唯一键来映射所有书籍,因此重复的可能性可以忽略不计,也可以0

我试图这样做:

    Map<String, Book> booksByAsinAndTitle = books.stream()
        .collect(Collectors.groupingBy((book) -> book.getAsin() + "||" + book.getTitle()))
        .entrySet()
        .stream()
        .collect(Collectors.toMap(x -> x.getKey(), x -> x.getValue().get(0)));

它可以工作,但是看起来很丑陋,很难阅读,而且在代码库中使用也不是很好,因为它可能会使我的同事们困惑。有没有更好的方法java 8来达到相同的结果?

3 个答案:

答案 0 :(得分:7)

使用toMap代替groupingBy

Map<String, Book> booksByAsinAndTitle = 
    books.stream()
         .collect(Collectors.toMap(b -> b.getAsin() + "||" + b.getTitle(),
                                   Function.identity()));

如果您根据其分组的键是唯一的,则没有理由使用groupingBy

如果您的键可能不是唯一的,并且您仍然希望Map包含与给定键匹配的第一个值,请添加合并功能:

Map<String, Book> booksByAsinAndTitle = 
    books.stream()
         .collect(Collectors.toMap(b -> b.getAsin() + "||" + b.getTitle(),
                                   Function.identity()),
                                   (a,b) -> a);

答案 1 :(得分:4)

如果您确定键是唯一的,则无需对书进行分组。

Map<String, Book> booksByAsinAndTitle = books.stream()
    .collect(Collectors.toMap(book -> book.getAsin() + "||" + book.getTitle(), x -> x));

答案 2 :(得分:2)

使用Map.putIfAbsentforEach的相同表示将更简单:

Function<Book, String> primaryKey = book -> book.getAsin() + "||" + book.getTitle();
Map<String, Book> booksByAsinAndTitle = new HashMap<>();
books.forEach(book -> booksByAsinAndTitle.putIfAbsent(primaryKey.apply(book), book));

注意 :这可确保在地图上找到的第一个book仍保留在地图中。