使用 java 8 流 API 的 HashMap 的平均计数?
Average counts from HashMap using java 8 stream API?
我有一个 Map
类型
public class MapUtils {
private Map<String, Integer> queryCounts = new HashMap<>();
public void averageCounters(){
int totalCounts = queryCounts.values().stream().reduce(0, Integer::sum);
queryCounts = queryCounts.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
(Map.Entry::getValue)/totalCounts
));
}
这不会编译并在这一行显示错误 (Map.Entry::getValue)/totalCounts
。我该如何解决?有没有更好的方法可以使用 Java 8 API?
获得超过 Map
的平均值
编辑:
这是更好的方法吗?
queryCounts.entrySet()
.forEach(entry -> queryCounts.put(entry.getKey(),
entry.getValue()/totalCounts));
如果你想就地修改,最好使用Map.replaceAll
而不是Stream API:
int totalCounts = queryCounts.values().stream()
.collect(Collectors.summingInt(Integer::intValue));
queryCounts.replaceAll((k, v) -> v/totalCounts);
但是在您的情况下,此解决方案存在问题,因为除法结果将四舍五入为 int
数字,因此您几乎总是会在结果中得到零。实际上,您的代码中存在相同的问题。您可能希望将 Map<String, Double>
作为结果类型。所以你可能需要创建一个全新的 Map
:
Map<String, Double> averages = queryCounts.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey,
e -> ((double)e.getValue())/totalCounts));
另一种方法是首先将 queryCounts
声明为 Map<String, Double>
。这样你就可以使用 replaceAll
:
double totalCounts = queryCounts.values().stream()
.collect(Collectors.summingDouble(Double::doubleValue));
queryCounts.replaceAll((k, v) -> v/totalCounts);
最后还有一个最有效但很脏的替代方案。您的代码假定在调用 averageCounters()
之后不需要原始(非平均)queryCounts
。因此,您可以将 queryCounts
保留为 Map<String, Integer>
(这比计数到 Map<String, Double>
更有效),但然后像这样更改 Map
值类型:
double totalCounts = queryCounts.values().stream()
.collect(Collectors.summingInt(Integer::intValue));
Map<String, Object> map = (Map<String, Object>)queryCounts;
map.replaceAll((k, v) -> ((Integer)v)/totalCounts);
Map<String, Double> averages = (Map<String, Double>)map;
queryCounts = null;
类似的技巧是 performed in JDK inside the Collectors.groupingBy
implementation.
我有一个 Map
类型
public class MapUtils {
private Map<String, Integer> queryCounts = new HashMap<>();
public void averageCounters(){
int totalCounts = queryCounts.values().stream().reduce(0, Integer::sum);
queryCounts = queryCounts.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
(Map.Entry::getValue)/totalCounts
));
}
这不会编译并在这一行显示错误 (Map.Entry::getValue)/totalCounts
。我该如何解决?有没有更好的方法可以使用 Java 8 API?
Map
的平均值
编辑: 这是更好的方法吗?
queryCounts.entrySet()
.forEach(entry -> queryCounts.put(entry.getKey(),
entry.getValue()/totalCounts));
如果你想就地修改,最好使用Map.replaceAll
而不是Stream API:
int totalCounts = queryCounts.values().stream()
.collect(Collectors.summingInt(Integer::intValue));
queryCounts.replaceAll((k, v) -> v/totalCounts);
但是在您的情况下,此解决方案存在问题,因为除法结果将四舍五入为 int
数字,因此您几乎总是会在结果中得到零。实际上,您的代码中存在相同的问题。您可能希望将 Map<String, Double>
作为结果类型。所以你可能需要创建一个全新的 Map
:
Map<String, Double> averages = queryCounts.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey,
e -> ((double)e.getValue())/totalCounts));
另一种方法是首先将 queryCounts
声明为 Map<String, Double>
。这样你就可以使用 replaceAll
:
double totalCounts = queryCounts.values().stream()
.collect(Collectors.summingDouble(Double::doubleValue));
queryCounts.replaceAll((k, v) -> v/totalCounts);
最后还有一个最有效但很脏的替代方案。您的代码假定在调用 averageCounters()
之后不需要原始(非平均)queryCounts
。因此,您可以将 queryCounts
保留为 Map<String, Integer>
(这比计数到 Map<String, Double>
更有效),但然后像这样更改 Map
值类型:
double totalCounts = queryCounts.values().stream()
.collect(Collectors.summingInt(Integer::intValue));
Map<String, Object> map = (Map<String, Object>)queryCounts;
map.replaceAll((k, v) -> ((Integer)v)/totalCounts);
Map<String, Double> averages = (Map<String, Double>)map;
queryCounts = null;
类似的技巧是 performed in JDK inside the Collectors.groupingBy
implementation.