簡體   English   中英

組內的組的平均值 - 流

[英]Average for group no within group - stream

如何使用流計算組的平均值。 下面的代碼我想轉換為流解決方案。

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();
}

但方法總結平均值,所以不是我所期望的。 如果可以通過分組恢復求和,則可以返回例外結果。

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);

你可以一次性完成,但需要一個自定義收集器......

你想要的東西:

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");

另一種方法是使用collect(supplier, accumulator, combiner) 根據官方教程 (參見class Averager )中的示例,您可以編寫自己的課程

  • 收集當前金額和唯一名稱
  • 處理每個Item元素以更新總和和唯一名稱集
  • 在並行處理的情況下組合該類的其他實例。

所以這樣的課可以看起來像

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)

但說實話,在沒有並行處理的情況下,我更喜歡使用簡單循環的自己的解決方案(可能沒有什么改進,因為你不需要在add集合之前調用contains ,無論如何add內部調用它)

public static double  averageForGroup(List<Item> items) {
    Set<String> uniqueGroups = new HashSet<>();
    double sum = 0;
    for (Item item : items) {
        uniqueGroups.add(item.getGroupName());
        sum += item.getValue();
    }
    return sum / uniqueGroups.size();
}
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為空items返回0

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM