2015-08-19 2 views
3

У меня есть структура вроде этого:Преобразование карты <Long, Список <Foo>> к списку <Foo>

Map<Long, List<Foo>> 

где класс Foo предоставляет метод:

Class Foo { 
    public List<Bar> getBars(); 
    public void setBars(List<Bar> bars); 
} 

Теперь я хочу, чтобы преобразовать эту карту в List параметризован с классом Foo, где каждый элемент в этом списке - это Foo экземпляр с агрегированными списками баров для заданного длинного значения. Например с картой:

{1: [Foo1, Foo2], 
2: [Foo3]} 

где

Foo1.bars = [Bar1, Bar2] 
Foo2.bars = [Bar3] 
Foo3.bars = [Bar4, Bar5] 

Я хочу, чтобы получить в результате:

[FooA, FooB] 

где

FooA.bars = [Bar1, Bar2, Bar3] 
FooB.bars = [Bar4, Bar5] 

Что бы наиболее элегантное решение для этого в Java 8? Некоторые из экземпляров Foo могут быть использованы повторно, если необходимо, поскольку они больше не используются после этой операции.

+0

Так Что проблема? – SacJn

+0

@PhilippSander Я пытаюсь отобразить записи базы данных из внутреннего соединения с одним отношением к моему объекту домена. – slakomy

+0

@PhilippSander попробовал обычное решение Java 7: я взял значения с карты. Затем перебирайте каждый список и каждый список перебирает его элементы для создания агрегированного объекта, но мне не нравится это решение. Я думал, можно ли с потоками. – slakomy

ответ

5

Предполагая, что у вас есть Foo(List<Bar> bars) конструктора это довольно легко:

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

List<Foo> result = map.values() 
    .stream() 
    .map(
     foos -> new Foo(foos.stream() 
        .flatMap(foo -> foo.getBars().stream()) 
        .collect(toList()))) 
    .collect(toList()); 

Мы принимаем поток исходных значений карты (мы не заботимся о ключах), которые являются списками Foo. Каждый такой список мы сглаживаем, чтобы получить поток Bar, собрать их, чтобы перечислить и передать этот список в конструктор Foo(List<Bar>), поэтому мы получаем новые объекты Foo. Наконец, мы собираем их на List.

Если вы не имеете Foo(List<Bar>), только сеттер, вы должны сначала создать пустой Foo, а затем использовать сеттер и вернуть созданный Foo:

List<Foo> result = map.values() 
    .stream() 
    .map(foos -> { 
      Foo f = new Foo(); 
      f.setBars(foos.stream().flatMap(
       foo -> foo.getBars().stream()).collect(toList())); 
      return f; 
     }) 
    .collect(toList()); 

Если вы не хотите, чтобы создать новый Foo объектов (например, есть дополнительные свойства, которые вы хотите сохранить), то лучше вводить не в setBars, но addBars метода (который добавляет новые полосы к уже существующим), как это:

public class Foo { 
    ... 
    public Foo addBars(List<Bar> bars) { 
     this.bars.addAll(bars); 
     return this; 
    } 
} 

Теперь вы можете использовать работу терминала reduce объединить FOOS:

List<Foo> result = map.values() 
     .stream() 
     .map(foos -> foos.stream() 
      .reduce((foo1, foo2) -> foo1.addBars(foo2.getBars())).get()) 
     .collect(toList()); 
+0

Можно ли сделать это аналогичным образом без такого конструктора? Или мне нужно остаться с решением, написанным в комментарии выше? – slakomy

+0

@slakomy Как вы добавляете список в объект Foo? – Flown

+0

@ Записан через сеттер - я редактировал начальную запись. – slakomy

Смежные вопросы