使用Java 8 Stream如何扩展和重新组合一个List of List?

6

我有一个Class A的列表,其中包含一个列表本身。

public class A {
    public double val;
    public String id;
    public List<String> names = new ArrayList<String>();
    public A(double v, String ID, String name)
    {
        val = v;
        id = ID;
        names.add(name);
    }

static public List<A> createAnExample()
{
    List<A> items  = new ArrayList<A>();

    items.add(new A(8.0,"x1","y11"));
    items.add(new A(12.0, "x2", "y21"));
    items.add(new A(24.0,"x3","y31"));
    items.get(0).names.add("y12");
    items.get(1).names.add("y11");
    items.get(1).names.add("y31");
    items.get(2).names.add("y11");
    items.get(2).names.add("y32");
    items.get(2).names.add("y33");
    return  items;
}

目标是对列表中每个 ID 的平均值进行求和。我使用了 Java 8 流将代码添加到 Main 函数中。 我的问题是,如何在不使用第二个数组和 for 循环的情况下以更优雅的方式重写它。
static public void main(String[] args) {
    List<A> items = createAnExample();

    List<A> items2 = new ArrayList<A>();
    for (int i = 0; i < items.size(); i++) {
        List<String> names = items.get(i).names;
        double v = items.get(i).val / names.size();
        String itemid = items.get(i).id;
        for (String n : names) {
            A item = new A(v, itemid, n);
            items2.add(item);
        }
    }
    Map<String, Double> x = items2.stream().collect(Collectors.groupingBy(item ->
            item.names.isEmpty() ? "NULL" : item.names.get(0), Collectors.summingDouble(item -> item.val)));
    for (Map.Entry entry : x.entrySet())
        System.out.println(entry.getKey() + " --> " + entry.getValue());
}
2个回答

4
你可以使用flatMap来完成它:
x = items.stream()
    .flatMap(a -> a.names.stream()
        .map(n -> new AbstractMap.SimpleEntry<>(n, a.val / a.names.size()))
    ).collect(groupingBy(
        Map.Entry::getKey, summingDouble(Map.Entry::getValue)
    ));

如果您经常遇到此类问题,请考虑使用静态方法创建一个Map.Entry

static<K,V> Map.Entry<K,V> entry(K k, V v) {
    return new AbstractMap.SimpleImmutableEntry<>(k,v);
}

那么你将会得到一个不那么冗长的 .map(n -> entry(n, a.val/a.names.size()))


1
在我开发的免费StreamEx库中,扩展了标准流API并添加了特殊操作,可以帮助构建这样复杂的映射。使用StreamEx,您可以像这样解决问题:
Map<String, Double> x = StreamEx.of(createAnExample())
    .mapToEntry(item -> item.names, item -> item.val / item.names.size())
    .flatMapKeys(List::stream)
    .grouping(Collectors.summingDouble(v -> v));

这里mapToEntry创建了一组映射条目流(称为EntryStream),其中键是名称列表,值是平均值。接下来我们使用flatMapKeys将键展开,保留值不变(因此我们有一组Entry<String, Double>流)。最后,我们将它们分组在一起,对于重复的键求和值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接