Карта потока Java 8 <String, List<string> > сумма значений для каждого ключа
Я не очень хорошо знаком с Java 8 (все еще учится) и смотрю, могу ли я найти что-то эквивалентное приведенному ниже коду с использованием потоков.
Приведенный ниже код в основном пытается получить соответствующее двойное значение для каждого значения в String, а затем суммирует его. В этом формате я не мог найти никакой помощи. Я не уверен, что использование потоков очистит код или сделает его более беспорядочным.
// safe assumptions - String/List (Key/Value) cannot be null or empty
// inputMap --> Map<String, List<String>>
Map<String, Double> finalResult = new HashMap<>();
for (Map.Entry<String, List<String>> entry : inputMap.entrySet()) {
Double score = 0.0;
for (String current: entry.getValue()) {
score += computeScore(current);
}
finalResult.put(entry.getKey(), score);
}
private Double computeScore(String a) { .. }
Ответы
Ответ 1
Вы также можете использовать метод forEach
вместе с API потока, чтобы получить результат, который вы ищете.
Map<String, Double> resultSet = new HashMap<>();
inputMap.forEach((k, v) -> resultSet.put(k, v.stream()
.mapToDouble(s -> computeScore(s)).sum()));
s → computeScore(s)
можно изменить, чтобы использовать ссылку на метод, т.е. T::computeScore
где T
- это имя класса, содержащего computeScore
.
Ответ 2
Map<String, Double> finalResult = inputMap.entrySet()
.stream()
.collect(Collectors.toMap(
Entry::getKey,
e -> e.getValue()
.stream()
.mapToDouble(str -> computeScore(str))
.sum()));
Выше код выполняет итерацию по карте и создает новую карту с теми же ключами и перед тем, как поместить значения, она сначала выполняет итерацию по каждому значению - это список, вычисляет счет через вызов computeScore()
по каждому элементу списка, а затем суммирует собранные оценки быть введено в значение.
Ответ 3
Как насчет этого:
Map<String, Double> finalResult = inputMap.entrySet()
.stream()
.map(entry -> new AbstractMap.SimpleEntry<String, Double>( // maps each key to a new
// Entry<String, Double>
entry.getKey(), // the same key
entry.getValue().stream()
.mapToDouble(string -> computeScore(string)).sum())) // List<String> mapped to
// List<Double> and summed
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)); // collected by the same
// key and a newly
// calulcated value
Вышеупомянутая версия может быть объединена с методом single collect(..)
:
Map<String, Double> finalResult = inputMap.entrySet()
.stream()
.collect(Collectors.toMap(
Entry::getKey, // keeps the same key
entry -> entry.getValue()
.stream() // List<String> -> Stream<String>
// then Stream<String> -> Stream<Double>
.mapToDouble(string -> computeScore(string))
.sum())); // and summed
Основные части:
-
collect(..)
выполняет уменьшение элементов, используя определенную стратегию с Collector
. -
Entry::getKey
- это ярлык для entry → entry.getKey
. Функция для отображения ключа. -
entry → entry.getValue().stream()
возвращает Stream<String>
-
mapToDouble(..)
возвращает DoubleStream. У этого есть суммарная sum(..)
операции sum(..)
которая суммирует элементы - вместе создает новое значение для Карты.
Ответ 4
Независимо от того, используете ли вы потоковое решение или решение на основе цикла, было бы полезно и добавить некоторую ясность и структуру для извлечения внутреннего цикла в метод:
private double computeScore(Collection<String> strings)
{
return strings.stream().mapToDouble(this::computeScore).sum();
}
Конечно, это также можно реализовать с помощью цикла, но... что именно точка: этот метод теперь можно вызывать либо во внешнем цикле, либо в значениях потока записей карты.
Внешний цикл или поток также можно было бы втянуть в метод. В приведенном ниже примере я немного обобщил это: тип ключей карты не имеет значения. Также значения не являются экземплярами List
или Collection
.
В качестве альтернативы принятому в настоящее время ответу, основанное на потоке решение здесь не заполняет новую карту, созданную вручную. Вместо этого он использует Collector
.
(Это похоже на другие ответы, но я думаю, что извлеченный метод computeScore
значительно упрощает иначе уродливые лямбды, которые необходимы для вложенных потоков)
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class ToStreamOrNotToStream
{
public static void main(String[] args)
{
ToStreamOrNotToStream t = new ToStreamOrNotToStream();
Map<String, List<String>> inputMap =
new LinkedHashMap<String, List<String>>();
inputMap.put("A", Arrays.asList("1.0", "2.0", "3.0"));
inputMap.put("B", Arrays.asList("2.0", "3.0", "4.0"));
inputMap.put("C", Arrays.asList("3.0", "4.0", "5.0"));
System.out.println("Result A: " + t.computeA(inputMap));
System.out.println("Result B: " + t.computeB(inputMap));
}
private <T> Map<T, Double> computeA(
Map<T, ? extends Collection<String>> inputMap)
{
Map<T, Double> finalResult = new HashMap<>();
for (Entry<T, ? extends Collection<String>> entry : inputMap.entrySet())
{
double score = computeScore(entry.getValue());
finalResult.put(entry.getKey(), score);
}
return finalResult;
}
private <T> Map<T, Double> computeB(
Map<T, ? extends Collection<String>> inputMap)
{
return inputMap.entrySet().stream().collect(
Collectors.toMap(Entry::getKey, e -> computeScore(e.getValue())));
}
private double computeScore(Collection<String> strings)
{
return strings.stream().mapToDouble(this::computeScore).sum();
}
private double computeScore(String a)
{
return Double.parseDouble(a);
}
}
Ответ 5
Я нашел это несколько короче:
value = startDates.entrySet().stream().mapToDouble(Entry::getValue).sum();