Java 8-Optional.flatMapとOptional.mapの違い


162

これら2つの方法の違いは何ですか:Optional.flatMap()およびOptional.map()

例をいただければ幸いです。



5
@AlexisC。リンクは、オプションではなく、ストリームのマップとflatMapに関するものです。
Eran

1
@Eranそれは重要ではありません、あなたがそれがストリームのためであるかどうかにかかわらず、map / flatMapがどのように機能するかを理解しているなら、それはオプションのために同じです。オペレーションがストリームでどのように機能するかを理解している場合は、この質問をするべきではありません。コンセプトは同じです。
Alexis C.

2
@AlexisC。あんまり。OptionalのflatMapは、StreamのflatMapとほとんど共通点がありません。
Eran

1
@Eran マップとflatMap の概念的な違いについて話しているので、との間Stream#flatMapで1対1の対応をとっていませんOptional#flatMap
Alexis C.

回答:


166

map関数が必要なオブジェクトをflatMap返す場合、または関数がを返す場合に使用しますOptional。例えば:

public static void main(String[] args) {
  Optional<String> s = Optional.of("input");
  System.out.println(s.map(Test::getOutput));
  System.out.println(s.flatMap(Test::getOutputOpt));
}

static String getOutput(String input) {
  return input == null ? null : "output for " + input;
}

static Optional<String> getOutputOpt(String input) {
  return input == null ? Optional.empty() : Optional.of("output for " + input);
}

どちらの印刷ステートメントも同じものを印刷します。


5
質問:[flat]Mapマッピング関数をinput == null?私の理解は、Optionalそれがない場合のソートカットです-[JavaDoc](docs.oracle.com/javase/8/docs/api/java/util/…)はこれを裏付けているようです-" 値が存在する場合は、適用してください。 ……
ボリスザスパイダー

1
@BoristheSpider Optional.of(null)!= Optional.empty()
Diego Martinoia

14
@DiegoMartinoia Optional.of(null)Exceptionです。Optional.ofNullable(null) == Optional.empty()
Boris the Spider

1
@BoristheSpiderはい、あなたは正しいです。私はあなたの質問に答えようとしましたが、私はそれをさらに不明確にしたと思います:概念的には、Optional.ofNullable(null)は空であってはなりませんが、実際には空であると見なされるため、map / flatmapは実行されません。
Diego Martinoia

1
getOutputOptまたはgetOutputのいずれかで入力をnullにしないでください
DanyalBurke 2017年

55

どちらもオプションのタイプから何かに機能を引き受けます。

map()「関数を適用しているとして、あなたが持っているオプションに」:

if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));

関数がの関数である場合はどうなりますT -> Optional<U>か?
あなたの結果は今Optional<Optional<U>>です!

それが何であるかflatMap()です:関数がすでにを返しているOptional場合flatMap()、少し賢く、ダブルラップせずにを返しOptional<U>ます。

これは、2つの機能熟語の構成だ:mapflatten


7

注:-以下は、mapおよびflatmap関数の図です。それ以外の場合、Optionalは主に戻り値の型としてのみ使用されるように設計されています。

ご存じのとおり、Optionalは単一のオブジェクトを含む場合と含まない場合があるコンテナの一種であるため、null値が予想される場所であればどこでも使用できます(Optionalを適切に使用すると、NPEが表示されない場合があります)。たとえば、null可能である可能性があるpersonオブジェクトを予期するメソッドがある場合、次のようなメソッドを記述できます。

void doSome(Optional<Person> person){
  /*and here you want to retrieve some property phone out of person
    you may write something like this:
  */
  Optional<String> phone = person.map((p)->p.getPhone());
  phone.ifPresent((ph)->dial(ph));
}
class Person{
  private String phone;
  //setter, getters
}

ここでは、オプションの型に自動的にラップされる文字列型を返しました。

人物クラスがこのようになった場合、つまり電話もオプションです

class Person{
  private Optional<String> phone;
  //setter,getter
}

この場合、map関数を呼び出すと、戻り値がOptionalにラップされ、次のようになります。

Optional<Optional<String>> 
//And you may want Optional<String> instead, here comes flatMap

void doSome(Optional<Person> person){
  Optional<String> phone = person.flatMap((p)->p.getPhone());
  phone.ifPresent((ph)->dial(ph));
}

PS; NullPointerExceptionsなしでは生きられない場合を除き、isPresent()でチェックせずにOptionalでgetメソッドを(必要な場合に)呼び出さないでください。


1
この例は、クラスPersonが誤用されてOptionalいるため、回答の性質を混乱させる可能性があると思います。Optionalこのようなメンバーで使用するAPIの意図に反しています-mail.openjdk.java.net/pipermail/jdk8-dev/2013-September/…を
8bitjunkie

@ 8bitjunkieのおかげでそのアウトを指し示すために、それはScalaのオプションとは異なり...
SandeepGodara

6

私を助けたのは、2つの関数のソースコードを確認したことです。

マップ -結果をオプションでラップします。

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value)); //<--- wraps in an optional
    }
}

flatMap-「生の」オブジェクトを返します

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Objects.requireNonNull(mapper.apply(value)); //<---  returns 'raw' object
    }
}

1
flatMap「生のオブジェクトを返す」とはどういう意味ですか?flatMapまた、「ラップされた」マッピング済みオブジェクトをで返しますOptional。違いは、の場合とでflatMap、マッパー機能でマッピングされたオブジェクトをラップOptionalしながら、mapそれ自体が内のオブジェクトをラップしますOptional
Derek Mahar、

@DerekMaharは私のコメントを編集したため、私の投稿を削除しました。投稿し直す必要はありません。
maxxyme '10 / 10/19

3
  • Optional.map()

すべての要素を受け取り、値が存在する場合は、関数に渡されます。

Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);

追加は:3つの値のいずれかがあるtruefalseに包まれたオプションの場合は、optionalValue存在していた、または空のオプションをそう。

単純に使用できる結果を処理する必要がない場合ifPresent()、戻り値はありません。

optionalValue.ifPresent(results::add); 
  • Optional.flatMap()

ストリームの同じ方法と同様に機能します。ストリームのストリームを平坦化します。違いは、値が提示された場合、関数に適用されることです。それ以外の場合は、空のオプションが返されます。

オプションの値関数呼び出しを作成するために使用できます。

メソッドがあるとします。

public static Optional<Double> inverse(Double x) {
    return x == 0 ? Optional.empty() : Optional.of(1 / x);
}

public static Optional<Double> squareRoot(Double x) {
    return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
}

次に、次のように逆行列の平方根を計算できます。

Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);

または、必要に応じて:

Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);

もしどちらかinverse()またはsquareRoot()戻りOptional.empty()、結果は空です。


1
これはコンパイルされません。どちらの式も、結果を割り当てるDoubleではなくOptional <Double>を返します。
JL_SO

@JL_SOあなたは正しい。なぜなら、inverseにはOptional<Double>戻り値の型として型があるからです。
nazar_art

3

はい。あなたは、あなたがネストされたOptionalsに直面している場合にのみ使用「flatMap」に必要。これがその例です。

public class Person {

    private Optional<Car> optionalCar;

    public Optional<Car> getOptionalCar() {
        return optionalCar;
    }
}

public class Car {

    private Optional<Insurance> optionalInsurance;

    public Optional<Insurance> getOptionalInsurance() {
        return optionalInsurance;
    }
}

public class Insurance {

    private String name;

    public String getName() {
        return name;
    }

}

public class Test {

    // map cannot deal with nested Optionals
    public Optional<String> getCarInsuranceName(Person person) {
        return person.getOptionalCar()
                .map(Car::getOptionalInsurance) // ① leads to a Optional<Optional<Insurance>
                .map(Insurance::getName);       // ②
    }

}

Streamと同様に、Optional#mapはOptionalでラップされた値を返します。これが、入れ子になったOptional-を取得する理由Optional<Optional<Insurance>です。そして②では、それを保険インスタンスとしてマッピングしたいと思います。それが悲劇が起こった方法です。ルートはネストされたオプションです。シェルに関係なくコア値を取得できる場合は、それを実行します。それがflatMapが行うことです。

public Optional<String> getCarInsuranceName(Person person) {
    return person.getOptionalCar()
                 .flatMap(Car::getOptionalInsurance)
                 .map(Insurance::getName);
}

結局のところ、Java8を体系的に研究したい場合は、Java 8 In Actionのみを推奨します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.