Я просматривал похожие вопросы здесь, но ни один из них не помогает мне с этой конкретной проблемой.

У меня есть сложный вложенный объект, содержащий списки списков:

class Container {
    private List<Foo> fooList;
}

class Foo {
    private List<Bar> barList;
}

class Bar {
    private List<Baz> bazList;
}

Когда я использую REST api, я возвращаю объект-контейнер, который, как я ожидаю, будет иметь один и только один экземпляр Baz . Когда я добираюсь до своего объекта Baz, мне нужно оценить предикат. Мне нужно защитить себя от нулей во время моего обхода.

Вот что я пробовал:

 Optional.ofNullable(container)
                .map(Container::getFooList)
                .map(List::stream)
                .map(Stream::findFirst)
                .flatMap(Function.identity())
                .map(Foo::getBarList)
                .map(List::stream)
                .map(Stream::findFirst)
                .flatMap(Function.identity())
                .map(Bar::getBazList)
                .map(List::stream)
                .flatMap(Stream::findFirst)
                .map(Baz::getCode)
                .equals(someBaz.getCode());

Теперь это работает, но выглядит ужасно, и я думаю, что должен быть способ получше. В частности, вызов функции идентификации кажется единственным способом, с помощью которого я могу использовать flatMap после моих вызовов findFirst.

Как я могу сделать это более лаконично?

0
FerdTurgusen 11 Сен 2021 в 01:49

3 ответа

Лучший ответ

Вам вообще не нужна функция идентификации. .map(f).flatMap(identity) совпадает с .flatMap(f):

Optional.ofNullable(container)
    .map(Container::getFooList)
    .map(List::stream)
    .flatMap(Stream::findFirst)
    .map(Foo::getBarList)
    .map(List::stream)
    .flatMap(Stream::findFirst)
    .map(Bar::getBazList)
    .map(List::stream)
    .flatMap(Stream::findFirst)
    .map(Baz::getCode)
    .map(x -> x.equals(someBaz.getCode()))
    .orElse(false)

Если это под вашим контролем, я бы также посоветовал сделать все, что дает вам Container, возвращать Optional<Container>, вместо того, чтобы создавать Optional<Container> встроенным.

Кроме того, поскольку stream никогда не возвращает null, вы можете опустить map для каждого getXXXList:

Optional.ofNullable(container)
    .map(Container::getFooList)
    .flatMap(x -> x.stream().findFirst())
    .map(Foo::getBarList)
    .flatMap(x -> x.stream().findFirst())
    .map(Bar::getBazList)
    .flatMap(x -> x.stream().findFirst())
    .map(Baz::getCode)
    .map(x -> x.equals(someBaz.getCode()))
    .orElse(false)
2
Sweeper 11 Сен 2021 в 00:15

Я думаю, что небольшая вспомогательная функция делает это намного понятнее:

    static <T> Optional<T> getOnlyItem(List<T> ts) {
        if (ts.size() > 1) {
            throw new RuntimeException("Expected singleton list: " + ts);
        }
        return ts.size() == 0 ? Optional.empty() : Optional.of(ts.get(0));
    }

boolean status =
        getOnlyItem(container.getFooList())
                .flatMap(foo -> getOnlyItem(foo.getBarList()))
                .flatMap(bar -> getOnlyItem(bar.getBazList()))
                .map(baz -> baz.getCode())
                .map(code -> code.equals(someBaz.getCode()))
                .orElse(false);
0
tgdavies 11 Сен 2021 в 00:10

Вы можете попробовать это. Основное различие заключается в том, что не пытается найти первый, пока все значения не будут переданы в потоковом режиме.

Container container = new Container();
Baz someBaz = new Baz("someCode");


boolean ret = container.getFooList().stream()
.flatMap(f->f.getBarList().stream())
.filter(Objects::nonNull)
.flatMap(b->b.getBazList().stream())
.findFirst()
.map(Baz::getCode).orElse("Unknown")
.equals(someBaz.getCode());
0
WJS 11 Сен 2021 в 00:26