2017-09-22 56 views
2

我有定製的Foo對象貓和犬的各自含有2名列表的列表的對象的對象列表,如下所示:合併含列表

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

class Cat { 
    int id; 
} 

class Dog { 
    String name; 
} 

我有一種方法,其中我通過在一個列表請求和我想將它平化爲一個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); 

的結果應該是:

美孚用List = catsForOne + catsForTwo和List = dogsForTwo

這是一個玩具的例子,但你可以想象FOO有約5-6個集合(即Cat,Dog,Pig,Duck等),我想用Java 8構建一個緊湊的解決方案。我可以做的一個天真的工作是循環請求並不斷添加所有一一收集到最終結果Foo。

假設Foo,Cat,Dog等來自外部庫,因此它們的源代碼不能改變。

+1

您能否提供請求來源清單的定義? – eg04lt3r

+0

'List list = new ArrayList <>(); for(Foo foo:fooList){list.addAll(foo.cats); list.addAll(foo.dogs); }列表展平。如果這不是你的意思,那就澄清這個問題。 – Andreas

+0

編輯了這個問題,希望它更清楚! –

回答

1

這樣做,

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

和lambda一樣,引入bug也非常容易。只要保持簡單並多寫幾行。

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); 
} 
+0

沒錯,這與我目前的想法很相似,但是想知道是否有一些簡潔的方式來使用Stream API或其他Java8功能來表達它。 –

+0

@JohnBaum是的,使用'reduce()'如[我的答案](https://stackoverflow.com/a/46373985/5221149)所示。方法名稱不重要。它可以被命名爲'flatten'就像這個答案一樣。 – Andreas

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

加一個簡單和閱讀這樣的代碼也很容易 – Eugene

1

合併多個Foo對象,使用Java 8流,實現合併方法:

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

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

現在,您可以很容易地合併在一個List所有Foo對象成爲單個Foo對象,使用reduce()流方法。

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

UPDATE

上面的代碼不是並聯使用安全的,因爲該方法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); 
+0

非常酷!但看到我的最終編輯:) –