Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 convert List to Lookup Map

I have a list of Station, in each Station there is a list of radios. I need to create a lookup Map of radio to Station. I know how to use Java 8 stream forEach to do it:

stationList.stream().forEach(station -> {
    Iterator<Long> it = station.getRadioList().iterator();
    while (it.hasNext()) {
        radioToStationMap.put(it.next(), station);
    }
});

But I believe there should be more concise way like using Collectors.mapping().

Anyone can help?

like image 495
Nico Avatar asked Apr 06 '17 14:04

Nico


3 Answers

This should work and you don't need third parties.

stationList.stream()
    .map(s -> s.getRadioList().stream().collect(Collectors.toMap(b -> b, b -> s)))
    .flatMap(map -> map.entrySet().stream())
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
like image 168
utkusonmez Avatar answered Oct 05 '22 22:10

utkusonmez


Based on the question, considering the entities Radio and Station to be defined as:

@lombok.Getter
class Radio {
    ...attributes with corresponding 'equals' and 'hashcode'
}

@lombok.Getter
class Station {
    List<Radio> radios;
    ... other attributes
}

One can create a lookup map from a List<Station> as an input using a utility such as:

private Map<Radio, Station> createRadioToStationMap(final List<Station> stations) {
    return stations.stream()
            // create entries with each radio and station
            .flatMap(station -> station.getRadios().stream()
                    .map(radio -> new AbstractMap.SimpleEntry<>(radio, station)))
            // collect these entries to a Map assuming unique keys
            .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey,
                    AbstractMap.SimpleEntry::getValue));
}

Slightly different from this behaviour, if for same(equal) Radio element across multiple Stations, one wants to group all such stations, it can be achieved using groupingBy instead of toMap such as :

public Map<Radio, List<Station>> createRadioToStationGrouping(final List<Station> stations) {
    return stations.stream()
            .flatMap(station -> station.getRadios().stream()
                    .map(radio -> new AbstractMap.SimpleEntry<>(radio, station)))
            // grouping the stations of which each radio is a part of
            .collect(Collectors.groupingBy(AbstractMap.SimpleEntry::getKey,
                    Collectors.mapping(AbstractMap.SimpleEntry::getValue, Collectors.toList())));
}
like image 34
Naman Avatar answered Oct 05 '22 22:10

Naman


If you are open to using a third-party library, there is the method groupByEach from Eclipse Collections:

Multimap<Radio, Station> multimap = 
    Iterate.groupByEach(stationList, Station::getRadioList);

This can also be written using Java 8 Streams with the Collectors2 utility from Eclipse Collections:

Multimap<Radio, Station> multimap =
        stationList.stream().collect(
                Collectors2.groupByEach(
                        Station::getRadioList,
                        Multimaps.mutable.list::empty));

Note: I am a committer for Eclipse Collections.

like image 30
Donald Raab Avatar answered Oct 05 '22 23:10

Donald Raab