我有一个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()
。
任何人都可以提供帮助吗?
答案 0 :(得分:4)
根据该问题,考虑将实体Radio
和Station
定义为:
@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();
答案 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");
});
});
});
最后一个代码段的问题是,您不能真正记录应归咎于非唯一性的radio
。 left
和right
是Stations
。如果您也想要这样做,则需要使用内部不依赖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)));
如果您不想使用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);