본문 바로가기

Development/Java

[JAVA] Stream API에서 NoSuchElementException을 방지하는 방법

728x90

Stream API를 적재적소에 사용하게 되면 코드의 가독성과 성능을 올릴 수 있는데요.

처음 접하고 사용하게 될 경우 NoSuchElementException을 빈번하게 발견할 수 있습니다.

마치 NPE(NullPointException)과 같이 항상 따라 오기도 하는데요.

NoSuchElementException을 방지하면서 사용하는 방법에 대해서 정리를 해볼게요.

 

NoSuchElementException란?

Oracle의 문서를 보면 다음과 같이 설명이 되어 있습니다.

Thrown by various accessor methods to indicate that the element being requested does not exist.

 

구글 번역에 의하면 "요청 중인 요소가 존재하지 않음을 나타내기 위해 다양한 접근자 메서드에 의해 발생됩니다." 라고 번역이 되네요.

쉽게 보면 어떤 대상을 호출할려고 하는데 호출할 대상을 찾을 수 없을 때 발생하는 에러라고 생각하면 될듯합니다.

Stream API외에도 발생되는 Exception이긴 하지만 Stream API에서 발생되는 부분에 대해서만 고려하겠습니다.

NoSuchElementException 예외 생성

Stream API에서 NoSuchElementException을 임의로 발생시켜 보겠습니다.

@Test
public void givenEmptyOptional_whenCallingGetMethod_thenThrowNoSuchElementException() {
    List<String> names = List.of("William", "Amelia", "Albert", "Philip");
    Optional<String> emptyOptional = names.stream()
            .filter(name -> name.equals("Emma"))
            .findFirst();

    assertThrows(NoSuchElementException.class, emptyOptional::get);
}

 

리스트로 "William", "Amelia", "Albert", "Philip"를 생성하고 

그 리스트에서 "Emma"를 필터하고 첫번째 요소를 반환하도록 Stream API로 구현하였습니다.

반환은 두가지 경우로 볼 수 있는데요. 찾은 요소가 있는 Optional 또는 빈 Optional을 얻게 될 것입니다.

 

하지만 실제로는 "Emma"라는 데이터는 없으므로 emptyOptional에는 빈 Optional을 반환하게 되어 emptyOptional.get()을 하게 되면 NoSuchElementException이 발생될 것입니다.

NoSuchElementException 예외 피하기

NoSuchElementException의 에러를 피하기 위해서는 여러가지 방법이 있지만 3가지 방법에 대해서 살펴보겠습니다.

 

첫번째, Optional에서 제공하는 isPresent()를 사용한다.

Optional에서는 isPresent()라는 매서드를 활용하여 Optional안에 데이터가 존재하는지 여부를 확인할 수 있습니다.

@Test
public void givenEmptyOptional_whenUsingIsPresentMethod_thenReturnDefault() {
    List<String> names = List.of("Tyler", "Amelia", "James", "Emma");
    Optional<String> emptyOptional = names.stream()
            .filter(name -> name.equals("Lucas"))
            .findFirst();

    String name = "unknown";
    if (emptyOptional.isPresent()) {
        name = emptyOptional.get();
    }

    assertEquals("unknown", name);
}

위의 예제 처럼 isPresent()를 활용할 경우 NoSuchElementException의 에러는 피했지만 if-else문 처럼 비용이 발생됨을 알 수 있습니다.

 

두번째, orElse() 매서드를 사용한다.

Optional에는 orElse() 메서드도 제공합니다. 이는 Optional에 요소(데이터)가 있을 경우 요소를 반환하며 없을 경우 orElse(대체값)을 사용하여 대체되는 값을 반환하도록 할 수 있습니다.

@Test
public void givenEmptyOptional_whenUsingOrElseMethod_thenReturnDefault() {
    List<String> names = List.of("Nicholas", "Justin", "James");
    Optional<String> emptyOptional = names.stream()
            .filter(name -> name.equals("Lucas"))
            .findFirst();

    String name = emptyOptional.orElse("unknown");

    assertEquals("unknown", name);
}

이렇게 할 경우 isPressent() 매서드를 활용할 때의 if-else문의 비용을 줄일수 있고, 가독성 또한 좋아진 것을 볼 수 있습니다.

 

세번째, orElseGet() 매서드를 활용한다.

orElse()와 동일한 기능을 제공하는 orElseGet() 매서드를 사용할 수 있습니다.

@Test
public void givenEmptyOptional_whenUsingOrElseGetMethod_thenReturnDefault() {
    List<String> names = List.of("Thomas", "Catherine", "David", "Olivia");
    Optional<String> emptyOptional = names.stream()
            .filter(name -> name.equals("Liam"))
            .findFirst();

    String name = emptyOptional.orElseGet(() -> "unknown");

    assertEquals("unknown", name);
}

orElse()와 orElseGet()의 차이점은 orElse()의 경우 모든 경우에 대해서 실행 된다면, orElseGet()에서는 Optional의 값이 없는 경우에만 실행이 됩니다. 또한 orElse()의 경우 명확한 값을 매개변수로 사용된다면, orElseGet()의 경우 함수 인터페이스를 매개변수로 사용 할수 있습니다.

 

3가지 방법 모두 한가지를 뜻합니다. 요소(데이터_값)의 존재유무를 확인한다는 점입니다.

NullPointException처럼 습관에 습득하시면 생각지도 못한 에러의 홍수에서 벗어날 것입니다.

 

감사합니다.

 

 

 

 

 

 

 

 

출처 : https://www.baeldung.com/java-streams-api-avoid-nosuchelementexception

728x90
반응형