如何使用流计算组的平均值。下面的代码我想转换为流解决方案。
public static void main(String[] args) {
List<Item> items = Arrays.asList(
new Item("A", 1.0),
new Item("A", 1.0),
new Item("B", 1.0)
);
System.out.println(averageForGroup(items));
}
public static double averageForGroup(List<Item> items) {
Set<String> uniqueGroups = new HashSet<>();
double sum = 0;
for (Item i : items) {
String groupName = i.getGroupName();
if (!uniqueGroups.contains(groupName)) {
uniqueGroups.add(groupName);
}
sum += i.getValue();
}
return sum / uniqueGroups.size();
}
物品类别:
public class Item {
private String groupName;
private Double value;
// Full-args constructor
// Getters and setters
}
我尝试过这样的事情:
public static double averageForGroup2(List<Item> items) {
return items.stream()
.collect(Collectors.groupingBy(
Item::getGroupName,
Collectors.averagingDouble(Item::getValue)) )
.entrySet().stream()
.mapToDouble(entry -> entry.getValue())
.sum();
}
但是方法总结了平均值,所以不是我所期望的。如果可以通过分组还原求和,则可能会返回例外结果。
答案 0 :(得分:5)
double result = items.stream()
.collect(
Collectors.collectingAndThen(
Collectors.groupingBy(
Item::getGroupName,
Collectors.summingDouble(Item::getValue)),
map -> map.values().stream().mapToDouble(Double::doubleValue).sum() / map.size()));
要使其更具可读性,您可以通过两种操作来做到这一点:
long distinct = items.stream().map(Item::getGroupName).distinct().count();
double sums = items.stream().mapToDouble(Item::getValue).sum();
System.out.println(sums / distinct);
您可以一次性完成,但需要自定义收集器...
答案 1 :(得分:1)
您想要类似的东西
Map<String, Double> map = items.stream() // Stream
.collect(Collectors.groupingBy( // Group to map
Item::getGroupName, // Key is the groupName
Collectors.averagingDouble(Item::getValue))); // Value is the average of values
要获取特定组的平均结果,请从Map
中获取值:
double averageForA = map.get("A");
答案 2 :(得分:1)
另一种方法是使用collect(supplier, accumulator, combiner)
。根据{{3}}的示例(请参见class Averager
),您可以编写自己的类,让您
这样的类看起来像
class ItemAverager {
Set<String> set = new HashSet();
double sum = 0;
ItemAverager add(Item item) {
set.add(item.getGroupName());
sum += item.getValue();
return this;
}
ItemAverager combine(ItemAverager ia) {
set.addAll(ia.set);
sum += ia.sum;
return this;
}
double average() {
if (set.size() > 0)
return sum / set.size();
else
return 0; //or throw exception
}
}
并且可以像
一样使用List<Item> items = Arrays.asList(
new Item("A", 1.0),
new Item("A", 3.0),
new Item("B", 1.0)
);
double avrg = items
.stream()
.collect(ItemAverager::new,
ItemAverager::add,
ItemAverager::combine
).average(); // `collect` will return ItemAverager
// on which we can call average()
System.out.println(avrg); // Output: 2.5
// (since 1+3+1 = 5 and there are only two groups 5/2 = 2.5)
但是老实说,在没有并行处理的情况下,我希望您使用自己的解决方案使用简单的循环(由于没有必要在集合contains
之前调用add
,{ {1}}仍在内部调用它
add
答案 3 :(得分:0)
public static double getAverageByGroups(List<Item> items) {
Map<String, Double> map = Optional.ofNullable(items).orElse(Collections.emptyList()).stream()
.collect(Collectors.groupingBy(Item::getGroupName, Collectors.summingDouble(Item::getValue)));
return map.isEmpty() ? 0 : map.values().stream().mapToDouble(value -> value).sum() / map.size();
}
在此示例中,getAverageByGroups
为空的0
返回items
。