java 8 groupBY或使用一般方法

时间:2016-07-19 19:45:13

标签: java

我得到了这个类的对象的arraylist。 e.g:

new Foo(1, "P1", 300, 400), 
new Foo(1, "P4", 300, 400),
new Foo(2, "P2", 600, 400),
new Foo(3, "P3", 30, 20),
new Foo(3, "P3", 70, 20),
new Foo(1, "P1", 360, 40),
new Foo(4, "P4", 320, 200),
new Foo(4, "P4", 500, 900)

我想通过创建一个新的FOO对象列表来转换这些值,这个对象具有按id和reference分组的求和值。 它会像:

new Foo(1, "P1", 660, 440), 
new Foo(1, "P4", 300, 400),
new Foo(2, "P2", 600, 400), 
new Foo(3, "P3", 100, 40)

2 个答案:

答案 0 :(得分:0)

如果您打算进行这种“特殊”分组,您应该(或者更确切地说:可以)不使用内置的groupBy功能。内置函数的语义不包括您要对Foo对象执行的“合并”操作。

相反,您可以使用Collectors#toMap功能:

  • keyMapper是一个为条目创建密钥,某种“标识符”的函数。在您的情况下,这是一个带Foo的函数,并创建一个Entry<Integer, String>对象,用作地图的关键字。 (您可以在那里使用任何PairTuple - 即使List<Object>也会这样做)
  • valueMapper可以是身份功能
  • mergeFunction是重要的一个:它需要两个Foo个对象(具有相同的数字和字符串),并返回一个新的合并Foo对象,其余的值已被添加。

结果将是Map,但如果需要,您可以根据此地图的值创建List

以下是一个例子:

import static java.util.stream.Collectors.toMap;

import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;

public class GroupByTest
{
    public static void main(String[] args)
    {
        example();
        exampleUnmaintainable();
    }


    private static void example()
    {
        List<Foo> foos = Arrays.asList(
            new Foo(1, "P1", 300, 400), 
            new Foo(1, "P4", 300, 400),
            new Foo(2, "P2", 600, 400),
            new Foo(3, "P3", 30, 20),
            new Foo(3, "P3", 70, 20),
            new Foo(1, "P1", 360, 40),
            new Foo(4, "P4", 320, 200),
            new Foo(4, "P4", 500, 900)
        );

        Function<Foo, Entry<Integer, String>> keyMapper = foo -> 
            new SimpleEntry<Integer, String>(foo.getId(), foo.getS());
        Function<Foo, Foo> valueMapper = foo -> foo;
        BinaryOperator<Foo> mergeFunction = (f0, f1) ->
            new Foo(f0.getId(), f0.getS(), f0.getX()+f1.getX(), f0.getY()+f1.getY());
        Map<Entry<Integer, String>, Foo> result = 
            foos.stream().collect(toMap(keyMapper, valueMapper, mergeFunction));

        for (Entry<Entry<Integer, String>, Foo> entry : result.entrySet())
        {
            System.out.println(entry);
        }
    }


    private static void exampleUnmaintainable()
    {
        List<Foo> foos = Arrays.asList(
            new Foo(1, "P1", 300, 400), 
            new Foo(1, "P4", 300, 400),
            new Foo(2, "P2", 600, 400),
            new Foo(3, "P3", 30, 20),
            new Foo(3, "P3", 70, 20),
            new Foo(1, "P1", 360, 40),
            new Foo(4, "P4", 320, 200),
            new Foo(4, "P4", 500, 900)
        );

        List<Foo> result = foos.stream().collect(toMap(
            foo -> new SimpleEntry<Integer, String>(foo.getId(), foo.getS()), 
            foo -> foo, (f0, f1) -> new Foo(f0.getId(), f0.getS(), 
                f0.getX()+f1.getX(), f0.getY()+f1.getY())))
            .values().stream().collect(Collectors.toList());

        for (Foo foo : result)
        {
            System.out.println(foo);
        }
    }


    static class Foo
    {
        private int id;
        private String s;
        private int x;
        private int y;

        public Foo(int id, String s, int x, int y)
        {
            this.id = id;
            this.s = s;
            this.x = x;
            this.y = y;
        }

        public int getId()
        {
            return id;
        }

        public String getS()
        {
            return s;
        }

        public int getX()
        {
            return x;
        }

        public int getY()
        {
            return y;
        }

        @Override
        public String toString()
        {
            return "Foo [id=" + id + ", s=" + s + ", x=" + x + ", y=" + y + "]";
        }
    }

}

是的,一个被动的攻击性评论:如果你不喜欢方法名称等,那么你应该向我们展示你实际的Foo类。我们在这里猜一下......

答案 1 :(得分:-1)

使用这样的方法来获取第一个列表中的每个项目,检查它是否存在于新列表中,如果它不是它只是将项目添加到新列表中,如果它然后添加项目的最后两个字段到项目,列表中存在相同的前两个字段。

public ArrayList<Foo> groupList(ArrayList<Foo> list){
    ArrayList<Foo> foos = new ArrayList<Foo>();
    for(Foo foo: list){
        Foo temp = getFoo(foos, foo);
        if(temp != null){
            temp.merge(foo);
        }else{
            foos.add(foo);
        }
    }
}

public Foo getFoo(ArrayList<Foo> foos, Foo foo){
    for(Foo temp: foos){
        if(foos.equals(temp)){
            return temp;
        }
    }
    return null;
}

并将这些方法添加到Foo课程中。 这些假设Foo的字段称为bar1bar2bar3bar4

public void merge(Foo otherFoo){
    this.setBar3(this.getBar3 + otherFoo.getBar3);
    this.setBar4(this.getBar4 + otherFoo.getBar4);
}

public boolean equals(Object object){
    if(object instanceof Foo){
        Foo otherFoo = (Foo)object;
        if(getBar1.equals(otherFoo.getBar1) && getBar2.equals(otherFoo.getBar2)){
            return true;
        }
    }
    return false;
}