#java-8 #java-stream
#java-8 #java-stream
Вопрос:
Пожалуйста, дайте мне знать, есть ли возможность изменить приведенный ниже код с точки зрения Java 8 с использованием параллельных потоков?
Я ищу возможность параллельного запуска «внешнего цикла for», и, наконец, все значения stationMap собираются вместе?
Map<Integer, Set<Integer>> stationMap = new HashMap<>();
Map<Integer, Set<Integer>> routes = function();
for (Map.Entry<Integer, Set<Integer>> entry : routes.entrySet())
{
Set<Integer> stations = entry.getValue();
for (Integer station : stations) {
Set<Integer> temporaryStations = new HashSet<>(stations);
Set<Integer> stationSet = stationMap.get(station);
if (stationSet == null) {
stationSet = new HashSet<>();
temporaryStations.remove(station);
stationSet.addAll(temporaryStations);
stationMap.put(station, stationSet);
} else {
temporaryStations.remove(station);
stationSet.addAll(temporaryStations);
}
}
}
Более короткая версия:
routes.forEach((k, stations) -> {
stations.forEach((station) -> {
Set<Integer> stationSet = stationMap.get(station);
if (stationSet == null) {
stationSet = new HashSet<>();
stationSet.addAll(stations);
stationMap.put(station, stationSet);
} else {
stationSet.addAll(stations);
}
});
});
Комментарии:
1. Прежде всего, не могли бы вы, пожалуйста, просмотреть свой код и рассказать нам, что вы делаете? Я думаю, что это можно упростить. Кажется, что-то здесь не так. Ответом будет использование чего-то подобного,
routes.keySet() .parallelStream() .forEach(key -> { final Set<Integer> station = routes.get(key); station .parallelStream() .forEach(v -> stationMap.put(v, station));; });
2. Большое вам спасибо. но есть небольшая проблема со stationMap.put(v, station)…. На самом деле я пытаюсь добавить предыдущие станции, когда помещаю набор станций в stationMap («stationSet.addAll (temporaryStations);»). Проблема теперь заключается в замене существующего набора
3. Я пытался получить предыдущие станции и добавить их, но это вызвало исключение одновременной модификации:(
4. Ваша сокращенная версия не выполняет то же самое, что и исходная.
5. Хорошо, это немного запоздало. Тем не менее, мой ответ также содержит решения, которые
<S1, <S1,S2,S3>>
сначала собираются, а затем удаляютсяS1
из<S1,S2,S3>
, поэтому вы можете решить, пропускать ли этот этап постобработки или нет…
Ответ №1:
Даже длинная версия до Java 8 может быть упрощена, поскольку нет необходимости выполнять итерации по набору записей, когда вы обрабатываете только значения, и нет необходимости в дублировании кода в двух условных ветвях:
Map<Integer, Set<Integer>> routes = function();
Map<Integer, Set<Integer>> stationMap = new HashMap<>();
for(Set<Integer> stations: routes.values()) {
for(Integer station: stations) {
Set<Integer> temporaryStations = new HashSet<>(stations);
temporaryStations.remove(station);
Set<Integer> stationSet = stationMap.get(station);
if (stationSet == null) {
stationMap.put(station, temporaryStations);
} else {
stationSet.addAll(temporaryStations);
}
}
}
используя функции Java 8, вы можете получить улучшенный вариант:
routes.values().forEach(stations ->
stations.forEach(station -> {
Set<Integer> temporaryStations = new HashSet<>(stations);
temporaryStations.remove(station);
Set<Integer> old = stationMap.putIfAbsent(station, temporaryStations);
if(old!=null) old.addAll(stations);
})
);
хотя, возможно, было бы проще сначала объединить все значения, а затем удалить ключи за один шаг:
routes.values().forEach(stations ->
stations.forEach(station ->
stationMap.computeIfAbsent(station, key -> new HashSet<>()).addAll(stations)
)
);
stationMap.forEach((k,set) -> set.remove(k));
Можно сформулировать эквивалентную (параллельную) потоковую операцию:
Map<Integer, Set<Integer>> stationMap=routes.values().parallelStream()
.flatMap(stations -> stations.stream().map(station -> {
Set<Integer> temporaryStations = new HashSet<>(stations);
temporaryStations.remove(station);
return new AbstractMap.SimpleImmutableEntry<>(station, temporaryStations);
})
).collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (a,b) -> {a.addAll(b); return a; }));
но это также может быть проще при удалении ключей из значения, установленного на этапе последующей обработки:
Map<Integer, Set<Integer>> stationMap=routes.values().parallelStream()
.flatMap(stations -> stations.stream().map(station ->
new AbstractMap.SimpleImmutableEntry<>(station, new HashSet<>(stations))
)
).collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (a,b) -> {a.addAll(b); return a; }));
stationMap.entrySet().parallelStream().forEach(e -> e.getValue().remove(e.getKey()));
или вы используете пользовательский коллектор вместо flatMap
:
Map<Integer, Set<Integer>> stationMap=routes.values().parallelStream()
.collect(HashMap::new,
(map,stations) -> stations.forEach(station ->
map.computeIfAbsent(station, key -> new HashSet<>()).addAll(stations)
),
(m1,m2) -> m2.forEach((k,v)->m1.merge(k, v, (a,b)->{a.addAll(b); return a;})));
stationMap.entrySet().parallelStream().forEach(e -> e.getValue().remove(e.getKey()));
это может быть более эффективным, поскольку для этого не нужны временные Map.Entry
экземпляры.