Java 8将List转换为Lookup Map

时间:2017-04-06 14:15:11

标签: java

我有一个Station列表,每个站都有一个无线电列表。我需要为Station创建一个无线电查找地图。我知道如何使用Java 8 stream forEach来做到这一点:

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

但我相信应该有更简洁的方式,例如使用Collectors.mapping()

任何人都可以提供帮助吗?

10 个答案:

答案 0 :(得分:4)

根据该问题,考虑将实体RadioStation定义为:

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

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

一个人可以使用实用程序从List<Station>作为输入来创建查找图:

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

与此行为略有不同,如果对于多个Radio中相同(相等)的Station元素,要对所有此类电台进行分组,则可以使用groupingBy而不是toMap,例如:

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

答案 1 :(得分:3)

这应该有效,不需要第三方。

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

答案 2 :(得分:1)

如果您愿意使用第三方库,则可以使用Eclipse Collections中的方法groupByEach

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

这也可以使用Java 8 Streams和Eclipse Collections中的Collectors2实用程序来编写:

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

注意:我是Eclipse Collections的提交者。

答案 3 :(得分:0)

与混合解决方案相比,我不认为你可以使用收藏家以更简洁的方式做到这一点

    stationList.stream().forEach(station -> {
        for ( Long radio : station.getRadioList() ) {
            radioToStationMap.put(radio, station);
        }
    });

    stationList.forEach(station -> {
        station.getRadioList().forEach(radio -> {
            radioToStationMap.put(radio, station);
        });
    });

(你可以直接在集合上调用.forEach,不需要经过.stream()

我能够提出的最短的完全'功能'解决方案就像是

 stationList.stream().flatMap(
     station -> station.getRadioList().stream().map(radio -> new Pair<>(radio, station)))
 .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));

使用第三方库中提供的任何Pair类。与Xtend或Groovy等方言相比,Java 8对于简单操作非常冗长。

答案 4 :(得分:0)

怎么样:

radioToStationMap = StreamEx.of(stationList)
        .flatMapToEntry(s -> StreamEx.of(s.getRadioList()).mapToEntry(r -> s).toMap())
        .toMap();

StreamEx

答案 5 :(得分:0)

就像在Artur Biesiadowski回答中一样,我认为您必须创建一个配对列表,然后将它们分组,至少如果您要考虑每个电台的无线电不是唯一的情况。

在C#中,您可以使用实用的匿名类,但是在Java中,您至少必须定义Pair类的接口

interface Radio{ }
interface Station {
    List<Radio> getRadioList();
}
interface RadioStation{
    Station station();
    Radio radio();
}

List<Station> stations = List.of();

Map<Radio,List<Station>> result= stations
   .stream()
   .flatMap( s-> s.getRadioList().stream().map( r->new RadioStation() {
        @Override
        public Station station() {
            return s;
        }

        @Override
        public Radio radio() {
            return r;
        }
    }  )).collect(groupingBy(RadioStation::radio, mapping(RadioStation::stations, toUnmodifiableList())));

答案 6 :(得分:0)

我们可以通过直接转换为SimpleEntry的流来将collectiong的中间步骤保存到Map中,例如:

Map<Long, Station> result = stationList.stream()
                .flatMap(station -> station.getRadioList().stream().map(radio -> new SimpleEntry<>(radio, station)))
                .collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue));

答案 7 :(得分:0)

您当然可以在没有Streams的情况下进行操作,这可能会使它更具可读性。

Map<Radio, Station> LOOK_UP = new HashMap<>();
List<Station> stations = ...


stations.forEach(station -> {
    station.getRadios().forEach(radio -> {
         LOOK_UP.put(radio, station);
    });
});

这与带有以下内容的普通循环没有太大区别:

for (Station station : stations) {
     for (Radio radio : station.getRadios()) {
          LOOK_UP.put(radio, station);
     }
}

这里明显的问题是LOOK_UP::put将始终替换某个键的值,从而掩盖了您重复的事实。例如:

[StationA = {RadioA, RadioB}]
[StationB = {RadioB}]

当您搜索RadioB时-结果是什么?

如果您有这种情况,很明显的事情是更改LOOK-UP的定义并使用Map::merge

    Map<Radio, List<Station>> LOOK_UP = new HashMap<>();
    List<Station> stations = new ArrayList<>();

    stations.forEach(station -> {
        station.getRadios().forEach(radio -> {
            LOOK_UP.merge(radio,
                          Collections.singletonList(station),
                          (left, right) -> {
                              List<Station> merged = new ArrayList<>(left);
                              merged.addAll(right);
                              return merged;
                          });
        });
    });

另一种可能性是当存在以下映射子项时抛出异常:

stations.forEach(station -> {
       station.getRadios().forEach(radio -> {
            LOOK_UP.merge(radio, station, (left, right) -> {
                 throw new RuntimeException("Duplicate Radio");
            });
       });
 });

最后一个代码段的问题是,您不能真正记录应归咎于非唯一性的radioleftrightStations。如果您也想要这样做,则需要使用内部不依赖Map::merge合并,就像this answer中那样。

因此,您可以看到,这完全取决于您需要如何以及如何处理。

答案 8 :(得分:0)

结果答案有些不同,但是我们可以使用Java9随附的flatMapping收集器来做到这一点。

这是您的电台课程-

class Station {
public List<String> getRadioList() {
    return radioList;
}

private List<String> radioList = new ArrayList<>();
}

以及要映射的电台列表-

        List<Station> list = new ArrayList<>();

下面的代码使您可以使用flatMapping收集器对其进行映射。

list.stream().collect(Collectors.flatMapping(station ->
                    station.getRadioList().stream()
                            .map(radio ->Map.entry( radio, station)),
            Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue), (radio, radio2) -> radio2)));
  1. 我们会将它们转换为Map.Entry
  2. 我们将与flatmapping收集器一起收集所有这些

如果您不想使用flatMapping,则实际上可以先使用FlatMap然后收集,它将更具可读性。

list.stream().flatMap(station -> station.getRadioList().stream().map(s -> Map.entry(s, station)))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (radio, radio2) -> radio2)));

答案 9 :(得分:-1)

使用flatMap查找的简单示例:

stationList.stream().flatMap(station->station.getRadioList().stream()).forEach(radion -> YourConsummer);