Java Потоки с дополнительным, как работает преобразование - PullRequest
4 голосов
/ 27 марта 2020

Я не могу понять концепцию необязательных и потоков при использовании с map () против flatMap ().

Я понимаю этот код:

public String getCarInsurance(Optional<Person> optPerson) {
    return optPerson.flatMap(Person::getCar) //Returns Optional<Car>
            .flatMap(Car::getInsurance) //Returns Optional<Insurance>, .map would give Optional<Optional<Insurance>>
            .map(Insurance::getName) //no need for flatmap because getName returns String, not Optional<String>
            .orElse("UNKNOWN"); //returns value or Unknown
}

Но я не могу понять этот код:

 public Set<String> getCarInsuranceNames(List<Person> persons) {
        return persons.stream()
                .map(Person::getCar) //Convert the list of persons into Stream Optional<Car> 
                .map(optCar -> optCar.flatMap(Car::getInsurance)) //I don't understand this line
                .map(optIns -> optIns.map(Insurance::getName))
                .flatMap(Optional::stream)
                .collect(toSet());
    }

изменить:

Почему в этом случае нам нужно .map(optCar -> optCar.flatMap(Car::getInsurance)), получить Stream Optional<Insurance>? Но в первом коде .map(Car::getInsurance) было достаточно, чтобы получить Optional<Insurance>.

Я понимаю, почему нам нужно .flatmap вместо .map в первом коде, но я не могу понять конвейер Stream во второй код.

Во втором коде .map(Person::getCar) возвращает Stream Optional<Car>, а затем мы используем .map(optCar -> optCar.flatMap(Car::getInsurance))>, чтобы получить Stream Optional<Insurance>. Почему бы .flatmap(Car::getInsurance) не получить нам Stream Optional<Insurance>? Это то, что я не понимаю.

edit: Классы доменов ниже, если необходимо.

public class Insurance {
    public String getName() {
        return name;
    }   
}

public class Car {
    Optional<String> name;
        public Optional<String> getName() {
            return name;
        }

        public class Person {
            Optional<Car> car;
            public Optional<Car> getCar(){
                return car;
            }
        }

Ответы [ 2 ]

3 голосов
/ 27 марта 2020

Если бы вы могли сохранить оба сегмента кода одинаково, это помогло бы вам понять

public String getCarInsurance(Optional<Person> optPerson) {
    return optPerson.flatMap(Person::getCar) //Returns Optional<Car>
            .flatMap(Car::getInsurance) //Returns Optional<Insurance>, .map would give Optional<Optional<Insurance>>
            .map(Insurance::getName) //no need for flatmap because getName returns String, not Optional<String>
            .orElse("UNKNOWN"); //returns value or Unknown
}

public Set<String> getCarInsuranceNames(List<Person> persons) {
    return persons.stream()
            .map(person -> person.getCar() // chained in a similar manner as above
                    .flatMap(Car::getInsurance)
                    .map(Insurance::getName))
            .flatMap(Optional::stream)
            .collect(Collectors.toSet());
}

или намного чище, если бы вы могли улучшить существующие контракты ваших API до

public Optional<String> getCarInsurance(Person person) {
    return person.getCar() //Returns Optional<Car>
            .flatMap(Car::getInsurance) //Returns Optional<Insurance>, .map would give Optional<Optional<Insurance>>
            .map(Insurance::getName); //no need for flatmap because getName returns String, not Optional<String>
}

public Set<String> getCarInsuranceNames(List<Person> persons) {
    return persons.stream()
            .flatMap(person -> getCarInsurance(person).stream())
            .collect(Collectors.toSet());
}
1 голос
/ 27 марта 2020

Это сделано, чтобы избежать вложения Optional. optCar имеет тип Optional<Car>. Если бы мы вызывали map(optCar -> optCar.map(Car::getInsurance)) вместо map(optCar -> optCar.flatMap(Car::getInsurance)), то в итоге мы получили бы Stream<Optional<Optional<Insurance>>>.

Но вложение необязательно не имеет большого смысла, если у нас есть optA типа Optional<A> и там это метод A#getB, который возвращает Optional<B>, тогда мы можем упростить код, вызвав optA.flatMap(A::getB), что приведет к Optional<B>:

Optional<B> optB = optA.flatMap(A::getB);
Optional<Optional<B>> optOptB = optA.map(A::getB); // don't do that

optA.flatMap(A::getB) работает интуитивно - если optA пусто или если optA имеет значение, но optA.getB() пусто, то результатом flatMap будет пустое Optional<B>. Только если optA имеет значение и optA.getB() не равно нулю, результат не будет пустым.

В первом фрагменте было достаточно flatMap, поскольку это операция на Optional, тогда как в второй фрагмент, над которым мы работаем Stream.

...