[java] `Optional.orElse ()`와`Optional.orElseGet ()`의 차이점

Optional<T>.orElse()Optional<T>.orElseGet()방법 의 차이점을 이해하려고 합니다.

orElse()방법에 대한 설명 은 “있는 경우 값을 반환하고, 그렇지 않으면 다른 값을 반환합니다.”입니다.

orElseGet()방법에 대한 설명 은 “있는 경우 값을 반환하고, 그렇지 않으면 다른 것을 호출하고 해당 호출의 결과를 반환합니다.”입니다.

orElseGet()메소드는 기본적으로 매개 변수를 취하지 않고을 리턴하는 공급 업체 기능 인터페이스를 사용합니다 T.

어떤 상황에서 사용해야 orElseGet()합니까? 당신이 방법을 가지고 있다면 T myDefault()optional.orElse(myDefault())오히려 하지 optional.orElseGet(() -> myDefault())않겠습니까?

orElseGet()람다 식의 실행을 나중에 또는 다른 것으로 연기하는 것 같지 않으므로 요점은 무엇입니까? (나는 그것이 안전한 반환 경우가 더 유용 할 것이라고 생각했을 Optional<T>get()발생하지 NoSuchElementExceptionisPresent()항상 반환 분명 사실 …하지만이 아니, 그것은 단지 반환 T등을orElse() ).

내가 놓친 다른 차이점이 있습니까?



답변

다음 두 가지 시나리오를 수행하십시오.

Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );

경우 opt값을 포함하지 않는, 두 사람은 참으로 동일합니다. 하지만 opt 않는 값을 포함, 얼마나 많은 Foo객체를 생성 할 것인가?

추신 : 물론이 예에서는 차이를 측정 할 수는 없지만 원격 웹 서비스 또는 데이터베이스에서 기본값을 가져와야하는 경우 갑자기 매우 중요합니다.


답변

짧은 답변:

  • orElse ()Optional.isPresent()값에 관계없이 원하는지 여부에 관계없이 항상 주어진 함수를 호출합니다.
  • orElseGet () 은 주어진 함수 만 호출 할 때Optional.isPresent() == false

실제 코드에서 필요한 리소스 를 얻는비용많이 드는 경우 두 번째 방법을 고려할 있습니다.

// Always get heavy resource
getResource(resourceId).orElse(getHeavyResource());

// Get heavy resource when required.
getResource(resourceId).orElseGet(() -> getHeavyResource()) 

자세한 내용은이 함수와 함께 다음 예제를 고려하십시오.

public Optional<String> findMyPhone(int phoneId)

차이점은 다음과 같습니다.

                           X : buyNewExpensivePhone() called

+——————————————————————————————————————————————————————————————————+——————————————+
|           Optional.isPresent()                                   | true | false |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElse(buyNewExpensivePhone())          |   X  |   X   |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) |      |   X   |
+——————————————————————————————————————————————————————————————————+——————————————+

optional.isPresent() == false, 두 가지 방법 사이에 차이가 없습니다. 그러나, optional.isPresent() == true, orElse()항상 당신이 원하는 여부 후속 함수를 호출합니다.

마지막으로 사용 된 테스트 케이스는 다음과 같습니다.

결과:

------------- Scenario 1 - orElse() --------------------
  1.1. Optional.isPresent() == true
    Going to a very far store to buy a new expensive phone
    Used phone: MyCheapPhone

  1.2. Optional.isPresent() == false
    Going to a very far store to buy a new expensive phone
    Used phone: NewExpensivePhone

------------- Scenario 2 - orElseGet() --------------------
  2.1. Optional.isPresent() == true
    Used phone: MyCheapPhone

  2.2. Optional.isPresent() == false
    Going to a very far store to buy a new expensive phone
    Used phone: NewExpensivePhone

암호:

public class TestOptional {
    public Optional<String> findMyPhone(int phoneId) {
        return phoneId == 10
                ? Optional.of("MyCheapPhone")
                : Optional.empty();
    }

    public String buyNewExpensivePhone() {
        System.out.println("\tGoing to a very far store to buy a new expensive phone");
        return "NewExpensivePhone";
    }


    public static void main(String[] args) {
        TestOptional test = new TestOptional();
        String phone;
        System.out.println("------------- Scenario 1 - orElse() --------------------");
        System.out.println("  1.1. Optional.isPresent() == true");
        phone = test.findMyPhone(10).orElse(test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("  1.2. Optional.isPresent() == false");
        phone = test.findMyPhone(-1).orElse(test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("------------- Scenario 2 - orElseGet() --------------------");
        System.out.println("  2.1. Optional.isPresent() == true");
        // Can be written as test::buyNewExpensivePhone
        phone = test.findMyPhone(10).orElseGet(() -> test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");

        System.out.println("  2.2. Optional.isPresent() == false");
        phone = test.findMyPhone(-1).orElseGet(() -> test.buyNewExpensivePhone());
        System.out.println("\tUsed phone: " + phone + "\n");
    }
}


답변

나는 문제를 위해 여기에 도달했다 쿠도 언급 한 .

나는 다른 사람들을 위해 내 경험을 공유하고 있습니다.

orElse또는 orElseGet질문입니다.

static String B() {
    System.out.println("B()...");
    return "B";
}

public static void main(final String... args) {
    System.out.println(Optional.of("A").orElse(B()));
    System.out.println(Optional.of("A").orElseGet(() -> B()));
}

인쇄물

B()...
A
A

orElse선택적 값과 상호 의존적으로 B () 값을 평가합니다. 따라서 orElseGet게으르다.


답변

조건 에서 새로운 가치를 얻기 위해 무언가를 평가하고 싶을 때 orElse와 가장 큰 차이점을 말할 것 입니다.orElseGetelse

이 간단한 예를 고려하십시오-

// oldValue is String type field that can be NULL
String value;
if (oldValue != null) {
    value = oldValue;
} else {
    value = apicall().value;
}

이제 위 예제 Optional를와 함께 사용하도록 변환 해 보겠습니다 orElse.

// oldValue is Optional type field
String value = oldValue.orElse(apicall().value);

이제 위 예제 Optional를와 함께 사용하도록 변환 해 보겠습니다 orElseGet.

// oldValue is Optional type field
String value = oldValue.orElseGet(() -> apicall().value);

orElse호출되면,이 apicall().value평가 방법에 전달됩니다. 반면, orElseGet평가 의 경우 oldValue는 비어있는 경우에만 발생합니다 . orElseGet게으른 평가를 허용합니다.


답변

다음 예제는 차이점을 보여 주어야합니다.

String destroyTheWorld() {
  // destroy the world logic
  return "successfully destroyed the world";
}

Optional<String> opt = Optional.empty();

// we're dead
opt.orElse(destroyTheWorld());

// we're safe    
opt.orElseGet(() -> destroyTheWorld());

답은 문서에도 나타납니다.

public T orElseGet(Supplier<? extends T> other):

존재하는 경우 값을 리턴하고, 그렇지 않으면 다른 호출을 호출 하고 해당 호출 결과를 리턴하십시오.

Supplier 되지 않습니다 경우 호출 할 Optional선물. 이므로,

public T orElse(T other):

존재하는 경우 값을, 그렇지 않으면 다른 값을 리턴하십시오.

other문자열을 반환하는 메서드 인 경우 호출되지만 Optional존재하는 경우 값이 반환되지 않습니다 .


답변

그 차이는 매우 미묘하며 많은주의를 기울이지 않으면 잘못된 방식으로 사용합니다.

가장 좋은 방법의 차이를 이해 orElse()하고 orElseGet()orElse()(가) 경우 항상 실행됩니다 Optional<T>이다 널 (null) 또는 하지 ,하지만 orElseGet()경우에만 실행됩니다 Optional<T>이다 널 (null)이 .

orElse 의 사전 의미는 다음같습니다 .- 존재하지 않는 부분을 실행하지만 여기서는 모순됩니다 (아래 예 참조).

    Optional<String> nonEmptyOptional = Optional.of("Vishwa Ratna");
    String value = nonEmptyOptional.orElse(iAmStillExecuted());

    public static String iAmStillExecuted(){
    System.out.println("nonEmptyOptional is not NULL,still I am being executed");
    return "I got executed";
    }

출력 : nonEmptyOptional이 NULL이 아니며 여전히 실행 중입니다.


    Optional<String> emptyOptional = Optional.ofNullable(null);
    String value = emptyOptional.orElse(iAmStillExecuted());
    public static String iAmStillExecuted(){
    System.out.println("emptyOptional is NULL, I am being executed, it is normal as
    per dictionary");
    return "I got executed";
    }

출력 : emptyOptional은 NULL이며 실행 중입니다. 사전마다 정상입니다.

의 경우 orElseGet(), 방법은 사전 의미에 따라 진행됩니다
. orElseGet()선택 사항이 null 인 경우에만 파트가 실행됩니다
.

벤치 마크 :

+--------------------+------+-----+------------+-------------+-------+
| Benchmark          | Mode | Cnt | Score      | Error       | Units |
+--------------------+------+-----+------------+-------------+-------+
| orElseBenchmark    | avgt | 20  | 60934.425  | ± 15115.599 | ns/op |
+--------------------+------+-----+------------+-------------+-------+
| orElseGetBenchmark | avgt | 20  | 3.798      | ± 0.030     | ns/op |
+--------------------+------+-----+------------+-------------+-------+

비고 : 우리의 특정 예에서 orElseGet()분명히 성능이 뛰어났습니다 orElse().

그것은 매우 기본적인 근거를 원하는 나 같은 사람들의 의심을 없애기를 바랍니다 🙂


답변

우선 두 방법의 선언을 확인하십시오.

1) OrElse : 논리를 실행하고 결과를 인수로 전달합니다.

public T orElse(T other) {
 return value != null ? value : other;
}

2) OrElseGet : 옵션 내부의 값이 null 인 경우 논리 실행

public T orElseGet(Supplier<? extends T> other) {
  return value != null ? value : other.get();
}

위 선언에 대한 설명 :
“Optional.orElse”의 인수는 선택적 (null, empty 또는 value)의 개체 값에 관계없이 항상 실행됩니다. “Optional.orElse”를 사용하는 동안 위에서 언급 한 사항을 항상 고려하십시오. 그렇지 않으면 다음 상황에서“Optional.orElse”를 사용하는 것이 매우 위험 할 수 있습니다.

위험 -1) 로깅 문제 : orElse 내부의 내용에 로그 문이 포함 된 경우 :이 경우 매번 로깅이 끝납니다.

Optional.of(getModel())
   .map(x -> {
      //some logic
   })
  .orElse(getDefaultAndLogError());

getDefaultAndLogError() {
  log.error("No Data found, Returning default");
  return defaultValue;
}

위험 -2) 성능 문제 : orelse 내부의 컨텐츠가 시간 집약적 인 경우 : 시간 집약적 인 컨텐츠는 모든 i / o 조작 DB 호출, API 호출, 파일 읽기가 될 수 있습니다. 이러한 내용을 orElse ()에 넣으면 시스템은 사용하지 않는 코드를 실행하게됩니다.

Optional.of(getModel())
   .map(x -> //some logic)
   .orElse(getDefaultFromDb());

getDefaultFromDb() {
   return dataBaseServe.getDefaultValue(); //api call, db call.
}

위험 -3) 불법 상태 또는 버그 문제 : orElse 내부의 콘텐츠가 일부 객체 상태를 변경하는 경우 : Optional.map 함수 내에서 같은 장소에 다른 객체를 사용하고있을 수 있으며 이는 치명적인 버그가 될 수 있습니다.

List<Model> list = new ArrayList<>();
Optional.of(getModel())
  .map(x -> {
  })
  .orElse(get(list));

get(List < String > list) {
   log.error("No Data found, Returning default");
   list.add(defaultValue);
   return defaultValue;
}

그러면 orElse ()로 언제 갈 수 있습니까?
기본값이 상수 객체 enum 인 경우 orElse를 사용하는 것이 좋습니다. 위의 모든 경우 Optional.orElse () 대신 Optional.orElseGet () (선택 사항에 비어 있지 않은 값이 포함 된 경우에만 실행)을 사용할 수 있습니다. 왜?? orElse에서는 기본 결과 값을 전달하지만 orElseGet에서는 Supplier를 전달하고 Supplier의 메소드는 Optional의 값이 null 인 경우에만 실행합니다.

주요 내용은 다음과 같습니다.

  1. “Optional.orElse”에 로그 문이 포함되어 있으면 사용하지 마십시오.
  2. 시간이 많이 걸리는 논리가 포함 된 “Optional.orElse”를 사용하지 마십시오.
  3. “Optional.orElse”가 일부 객체 상태를 변경하는 경우 사용하지 마십시오.
  4. 상수 enum을 반환해야하는 경우“Optional.orElse”를 사용하십시오.
  5. 1, 2 및 3 포인트에서 언급 된 상황에서“Optional.orElseGet”을 선호하십시오.

나는 나의 중간 블로그 에서 point-2 ( “Optional.map/Optional.orElse”! =“if / else” ) 에서 이것을 설명했다 . 코더가 아닌 프로그래머로 Java8 사용