Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create List<T> to Map<String, T> instead of Map<String, List<T>>?

I encountered with the case when I need to convert List<Book> to Map<String, Book> and the only solutions I can find is how to do Map<String, List<Book>>.

The class itself looks the following way (I ommitted getters/setters and constructors):

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

I want to map all books by certain unique keys, so probability of duplication is either neglectable or 0.

I tried to do it this way:

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

It works but it looks ugly, hardly readable and not very nice to have in the codebase because it may confuse my colleagues. Is there any better way java 8 way to achieve the same result?

like image 721
Dmytro Chasovskyi Avatar asked Feb 11 '19 13:02

Dmytro Chasovskyi


3 Answers

Use toMap instead of groupingBy:

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

If the key according to which you are grouping is unique, there's no reason to use groupingBy.

If your key may not be unique, and you still want the Map to contain the first value matching a given key, add a merge function:

Map<String, Book> booksByAsinAndTitle = 
    books.stream()
         .collect(Collectors.toMap(b -> b.getAsin() + "||" + b.getTitle(),
                                   Function.identity()),
                                   (a,b) -> a);
like image 187
Eran Avatar answered Oct 12 '22 14:10

Eran


You don't need to group your books if you are sure that the keys are unique.

Map<String, Book> booksByAsinAndTitle = books.stream()
    .collect(Collectors.toMap(book -> book.getAsin() + "||" + book.getTitle(), x -> x));
like image 37
Sergii Lagutin Avatar answered Oct 12 '22 14:10

Sergii Lagutin


A simpler representation of the same using Map.putIfAbsent and forEach would be :

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

Note: This ensures that the first book found against a key remains in the map.

like image 29
Naman Avatar answered Oct 12 '22 12:10

Naman