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


206

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() ).

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


7
그 이유는 orElseGet가치가없는 경우에만 공급자에게 전화를 걸기 때문입니다 .
Alex Salauyou

9
아 알았어. 그렇게하는 경우 방법은 여전히 호출되지만 반환 값은 단지 사용되지 않습니다. orElse()myDefault()
jbx

3
내가 오해를 보았거나 단순히 사용을 잊어 버린 것으로 orElseGet()인해 심각한 버그가 발생할 수 있기 때문에 의문의 여지가있는 질문 : medium.com/alphadev-thoughts/…
softarn

좋은 설명이 여기에 있습니다 : baeldung.com/java-optional-or-else-vs-or-else-get
Nestor Milyaev

답변:


172

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

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

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

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


22
설명을 해주셔서 감사합니다. 따라서 그 차이는 미묘하지만 중요합니다. 두 번째 경우에는 새 Foo객체를 만들지 않지만 첫 번째 경우에는 객체를 만들지 만 내부에 값이 있으면 사용하지 않습니다 Optional.
jbx

5
@jbx 네, 제 노디 예제에서는 실제로 큰 차이를 만들지는 않지만 원격 웹 서비스 또는 데이터베이스에서 기본값을 가져와야하는 경우 그 차이는 갑자기 매우 중요합니다.
biziclop

2
@ jbx : 두 가지를 섞고 있습니다. SO에 관한 계산 결과를 사용하지 않아서 발생한 이상한 벤치 마크 결과에 대해서는 이미 의문이 있습니다. JVM 그렇게 할 수 있습니다 . 한편, System.out.println()이다 하지 계산하지만 관찰 부작용을 생성하는 문. 그리고 나는 이미 관찰 가능한 부작용이 최적화를 방해 할 것이라고 말했다 (콘솔 출력 스트림 외부 리소스).
Holger

7
답변 대신에 질문이 접수 된 것이 처음입니다.
Kirill G.

4
" 예를 들어 원격 웹 서비스에서 기본값을 얻어야하는 경우 "이 시나리오는 정확히 나의 시나리오였습니다. 필자의 경우 선택 사항은 쿼리였으며 쿼리가없는 경우 기본값은 모든 값을 가져 오는 것이 었습니다 ... 그렇습니다.
scottysseus

109

짧은 답변:

  • 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");
    }
}

사진에 오류가있는 것 같습니다. 오른쪽에 "orElseGet"이라고 표시되어 있습니까? 그 외에도 훌륭한 예입니다.
Yalla T.

그래 정확 해. 감사합니다 :) 다음 몇 시간 안에 업데이트하겠습니다
nxhoaf

두 번째 글 머리 기호의 경우, Optional.isPresent() == false대신 해야합니다 (거짓, 사실이 아님)
Manuel Jordan

좋은 예 -하지만 난 정말하지 않는 방법에 대한 Javadoc과 Optional.orElse국가가하는 If a value is present, returns the value, otherwise returns other이 동작을 의미 할 수 ...
에릭 Finnman

당신의 설명을 바탕으로, 날 위해 그 모양 orElse()과 유사한 동작합니다 finally에서 try-catch식입니다. 나 맞아?
Mike B.

63

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

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

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게으르다.


7
'문제'가 아닙니다. 메소드의 인수는 메소드 실행 전에 평가된다는 것은 간단한 사실입니다. B()명명 된 메소드에 전달 orElse()하거나 abc()차이가없는 경우 B()평가됩니다.
jbx 2012 년

11
여기서 문제는 실제로 메소드의 이름을 지정하는 것입니다. or그것이 우리가 부울 조건에 사용되는 것입니다 있기 때문에, 짧은 단락 동작이라고 생각에 (내가 문제를 물었을 때 자신을 포함) 접두사 오도 개발자. 그러나 이는 or접두사 가있는 메소드 이름 일 뿐이 므로 Optional값을 전달 하는지 여부에 관계없이 인수가 평가됩니다 . 우리가 그것에 대해 아무것도 할 수 없다는 것이 아니라 이름이 혼동된다는 것은 불행한 일입니다.
jbx

37

조건 에서 새로운 가치를 얻기 위해 무언가를 평가하고 싶을 때 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게으른 평가를 허용합니다.


4
ifElse ()의 "이상한"동작으로 인해 많은 시간을 낭비했습니다. ifElse ()보다 ifElseGet ()을 선호하는 것이 합리적이라고 말하고 싶습니다.
Enrico Giurin

3

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

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존재하는 경우 값이 반환되지 않습니다 .


3

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

가장 좋은 방법의 차이를 이해 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().

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


2

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

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 사용


0

다음 코드를 고려하십시오.

import java.util.Optional;

// one class needs to have a main() method
public class Test
{
  public String orelesMethod() {
    System.out.println("in the Method");
    return "hello";
  }

  public void test() {
    String value;
    value = Optional.<String>ofNullable("test").orElseGet(this::orelesMethod);
    System.out.println(value); 

    value = Optional.<String>ofNullable("test").orElse(orelesMethod());
    System.out.println(value); 
  }

  // arguments are passed using the text field below this editor
  public static void main(String[] args)
  {
    Test test = new Test();

    test.test();
  }
}

우리가 얻는 경우에 value이 방법으로 : Optional.<String>ofNullable(null), orElseGet () 및 OrElse라는 () 사이에 차이가 없지만, 우리가 얻는 경우에 value이 방법으로 : Optional.<String>ofNullable("test"), orelesMethod()orElseGet()호출되지 않습니다 만에 orElse()이를 호출됩니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.