合并包含列表

时间:2017-09-22 22:03:16

标签: java java-8

我有一个自定义Foo对象列表,每个对象包含2个Cat和Dog对象列表,如下所示:

class Foo {
   List<Cat> cats;
   List<Dog> dogs;
}

class Cat {
   int id;
}

class Dog {
   String name;
}

我有一个方法,我传入一个List请求,我想把它压平到一个Foo对象,其中包含每个请求狗和猫拼合在一起。有一种干净紧凑的方式吗?

来源清单:

List<Cat> catsForOne = new ArrayList<>(); // add a bunch of cats
List<Cat> catsForTwo = new ArrayList<>(); // add a bunch of cats
List<Dog> dogsForTwo = new ArrayList<>(); // add a bunch of dogs

List<Foo> requests = new ArrayList<>();
Foo one = Foo.builder().cats(catsForOne).build();
Foo two = Foo.builder().dogs(dogsForTwo).cats(catsForTwo).build();

requests.add(one);
requests.add(two);

结果应该是:

Foo with List = catsForOne + catsForTwo and a List = dogsForTwo

这是一个玩具示例,但你可以想象foo有大约5-6个集合(即Cat,Dog,Pig,Duck等),我想拥有一个Java 8紧凑的解决方案。天真的那个我可以做的是遍历请求并继续将所有集合逐个添加到最终结果Foo。

假设Foo,Cat,Dog等来自外部库,因此无法更改其源代码。

4 个答案:

答案 0 :(得分:5)

您可以单独收集它们,然后创建一个新的Foo对象并将lists分配给它。

 List<Dog> collectDogs = foos.stream().flatMap(foo -> foo.getCats().stream())
                       .collect(Collectors.toList());
 List<Cat> collectCats = foos.stream().flatMap(foo -> foo.getDogs().stream())
                      .collect(Collectors.toList());

然后

Foo result = new Foo();
result.setDogs(collectDogs);
result.setCats(collectCats);

答案 1 :(得分:1)

要使用Java 8流合并多个Foo对象,请实现合并方法:

class Foo {
    List<Cat> cats;
    List<Dog> dogs;

    Foo merge(Foo other) {
        this.cats.addAll(other.cats);
        this.dogs.addAll(other.dogs);
        return this;
    }
}

现在,您可以使用reduce()流方法轻松地将Foo中的所有List个对象合并到一个Foo对象中。

List<Foo> requests = ...;
Foo merged = requests.stream().reduce(new Foo(), Foo::merge);

<强>更新

上述代码对于并行使用是不安全的,因为merge()方法更新了当前对象。对于并行使用,Foo对象应视为不可变。

此外,增强了一个问题,即类Foo来自外部库,并且无法更改,因此merge()方法需要分开。

所以,这样做:

class Test {
    public static void test() {
        List<Foo> requests = ...;
        Foo merged = requests.stream().reduce(new Foo(), Test::mergeFoos);
    }

    public static Foo mergeFoos(Foo a, Foo b) {
        Foo merged = new Foo();
        merged.cats = mergeLists(a.cats, b.cats);
        merged.dogs = mergeLists(a.dogs, b.dogs);
        merged.pigs = mergeLists(a.pigs, b.pigs);
        merged.ducks = mergeLists(a.ducks, b.ducks);
        return merged;
    }
    private static <E> List<E> mergeLists(List<E> a, List<E> b) {
        List<E> merged = new ArrayList<>(a.size() + b.size());
        merged.addAll(a);
        merged.addAll(b);
        return merged;
    }
}

如果您不希望在列表为空时创建Foo,则可以这样执行:

Optional<Foo> merged = requests.stream().reduce(Test::merge);

答案 2 :(得分:1)

这样做,

Foo foo = requests.stream().reduce(new Foo(), (f1, f2) -> {
            f1.cats.addAll(f2.cats);
            f1.dogs.addAll(f2.dogs);
            return f1;
        });

与lambda一样,很容易引入bug。只需保持简单并写一些额外的行。

答案 3 :(得分:0)

向Foo添加一个函数,允许您将列表添加到一起,然后遍历您的Foos,将它们添加到一个真正的“扁平”Foo。

class Foo
{
  ...
  flatten(Foo a)
  {
    cats.addAll(a.cats);
    dogs.addAll(a.dogs);
  }
}

...

Foo result = new Foo();
for(Foo a: fooList)
{
  result.flatten(a);
}