简体   繁体   English

如何使用Java 8 Stream扩展并重新组合List列表?

[英]How to expand and do regroup a List of List using Java 8 Stream?

I have a list of the Class A, that includes a List itself. 我有一个A类列表,其中包含一个List本身。

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

The aim is to sum over average val per id over the List. 目的是对列表中每个id的平均val求和。 I added the code in Main function by using some Java 8 stream. 我使用一些Java 8流在Main函数中添加了代码。 My question is how can I rewrite it in a more elegant way without using the second Array and the for loop. 我的问题是如何在不使用第二个数组和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());
}

You can do it with flatMap : 你可以用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)
    ));

If you find yourself dealing with problems like these often, consider a static method to create a Map.Entry : 如果您发现自己经常处理这些问题,请考虑使用静态方法来创建Map.Entry

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

Then you would have a less verbose .map(n -> entry(n, a.val/a.names.size())) 然后你会有一个不那么详细的.map(n -> entry(n, a.val/a.names.size()))

In my free StreamEx library which extends standard Stream API there are special operations which help building such complex maps. 在我的免费StreamEx库中,它扩展了标准的Stream API,有一些特殊的操作可以帮助构建这样复杂的地图。 Using the StreamEx your problem can be solved like this: 使用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));

Here mapToEntry creates stream of map entries (so-called EntryStream ) where keys are lists of names and values are averaged vals. 这里mapToEntry创建映射条目流(所谓的EntryStream ),其中键是名称列表,值是平均值。 Next we use flatMapKeys to flatten the keys leaving values as is (so we have stream of Entry<String, Double> ). 接下来我们使用flatMapKeysflatMapKeys键,保持原样(因此我们有Entry<String, Double> )。 Finally we group them together summing the values for repeating keys. 最后,我们将它们组合在一起,对重复键的值进行求和。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM