Манипулирование хэш-картой с использованием потоков Java 8

#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 экземпляры.