Может ли поток создавать новые объекты? - PullRequest
3 голосов
/ 19 сентября 2019

Я постараюсь упростить ситуацию, с которой сталкиваюсь.Есть 2 сущности:

class Foo {
    String fooProperty;

    @OneToOne
    Bar bar;
}

class Bar {
    String barProperty;
}

Существует также класс DTO с атрибутами из Foo и Bar:

class FooDto {
    String fooProperty;
    String barProperty;

    public static FooDto from(Foo foo) {
        return new FooDto(foo.fooProperty, foo.bar.barProperty);
    }
}

FooDto используется для доставки огромных объемов данных, и преобразование можетлегко сделать это с помощью потоков:

fooList.stream().map(FooDto::from).(...)

Теперь в требованиях есть изменения, и Foo может иметь много баров:

@OneToMany
List<Bar> bar;

Также требуется, чтобы он генерировался одинFooDto для каждого содержащего Bar, например, так:

Foo(fooProperty="foo1", bar=[Bar(barProperty="bar1"), Bar(barProperty="bar2")])

будет преобразовано в:

[FooDto(fooProperty="foo1", barProperty="bar1"), FooDto(fooProperty="foo1", barProperty="bar2")]

Мне удалось решить его с помощью промежуточного списка, но я все еще не уверен, что это был лучшийрешение.

Мне было интересно, можно ли сделать что-то подобное, используя чисто потоки.Я могу использовать карты и фильтры и генерировать результат с равным / меньшим размером, чем исходный ввод, но я не могу понять, как генерировать результат с большим количеством элементов.

Ответы [ 2 ]

3 голосов
/ 20 сентября 2019

Операция, которую вы ищете, называется flatMap.

Она работает с потоком объектов Foo, принимает в качестве аргумента функцию, которая преобразует объект Foo в поток FooDto объектов и возвращает поток объединенный поток из FooDto объектов:

class FooDto {
    String fooProperty;
    String barProperty;

    public FooDto(String fooProperty, String barProperty) {
        this.fooProperty = fooProperty;
        this.barProperty = barProperty;
    }

    public static Stream<FooDto> from(Foo foo) {
        return foo.bars.stream().map(bar -> new FooDto(foo.fooProperty, bar.barProperty));
    }
}

, а затем:

List<FooDto> result = fooList.stream().flatMap(FooDto::from).(Collectors.toList());
0 голосов
/ 19 сентября 2019

Да, это возможно через потоки, вам просто нужно объединить faltMap с map

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

class Foo {
    String fooProperty;
    List<Bar> bars;

    public Foo(String fooProperty, List<Bar> bars) {
        this.fooProperty = fooProperty;
        this.bars = bars;
    }
}

class Bar {
    String barProperty;

    public Bar(String barProperty) {
        this.barProperty = barProperty;
    }
}

class FooDto {
    String fooProperty;
    String barProperty;

    public FooDto(String fooProperty, String barProperty) {
        this.fooProperty = fooProperty;
        this.barProperty = barProperty;
    }

    public static List<FooDto> from(List<Foo> foos) {
        return foos.stream().
                            flatMap(foo -> foo.bars.stream().
                                    map(bar -> new FooDto(foo.fooProperty, bar.barProperty))).
                            collect(Collectors.toList());
    }


}

в качестве примера:

public static void main(String[] args) {
    Bar b1 = new Bar("b1");
    Bar b2 = new Bar("b2");
    Bar b3 = new Bar("b3");
    Bar b4 = new Bar("b4");

    Foo foo = new Foo("Foo 1", Arrays.asList(b1,b2));
    Foo foo2 = new Foo("Foo 2", Arrays.asList(b2,b3,b4));
    List<FooDto> result = FooDto.from(Arrays.asList(foo, foo2));
    result.forEach(f -> System.out.println(f.fooProperty + ": " + f.barProperty));
}

Вывод

Foo 1: b1
Foo 1: b2
Foo 2: b2
Foo 2: b3
Foo 2: b4
...