Java 8의 다중 널 검사


99

여러 null 검사에 대해 약간 추악한 아래 코드가 있습니다.

String s = null;

if (str1 != null) {
    s = str1;
} else if (str2 != null) {
    s = str2;
} else if (str3 != null) {
    s = str3;
} else {
    s = str4;
}

그래서 Optional.ofNullable아래와 같이 사용해 보았지만 누군가 내 코드를 읽으면 여전히 이해하기가 어렵습니다. Java 8에서이를 수행하는 가장 좋은 방법은 무엇입니까?

String s = Optional.ofNullable(str1)
                   .orElse(Optional.ofNullable(str2)
                                   .orElse(Optional.ofNullable(str3)
                                                   .orElse(str4)));

Java 9 에서는와 Optional.ofNullable함께 사용할 수 있지만 ORJava8에는 다른 접근 방식이 있습니까?


4
Java9 or구문 String s = Optional.ofNullable(str1) .or(() -> Optional.ofNullable(str2)) .or(() -> Optional.ofNullable(str3)) .orElse(str4);Stream.ofI sya 만큼 좋지 않습니다 .
Naman

2
@ OleV.V. 잘못된 것은 없습니다. OP는 이미이를 인식하고 있으며 Java-8에 특정한 것을 찾고 있습니다.
Naman

3
사용자가 Java-8 특정 솔루션을 요구하고 있다는 것을 알고 있지만 일반적인 참고 사항으로 저는StringUtils.firstNonBlank()
Mohamed Anees A

3
문제는 Java 8 / 스트림이 이에 대한 최상의 솔루션이 아니라는 것입니다. 그 코드는 정말로 리팩터링이 필요한 것 같은 냄새가납니다.하지만 더 많은 컨텍스트가 없으면 말하기가 정말 어렵습니다. 우선, 컬렉션에 아직 너무 밀접하게 관련된 세 개의 개체가없는 이유는 무엇입니까?
Bill K

2
@MohamedAneesA는 (주석으로) 최상의 답변을 제공했지만이 경우 StringUtils의 소스를 지정하지 않았습니다. 여하튼 이들을 별도의 문자열로 묶어야한다면 "firstNonBlank"와 같은 vargs 메소드로 코딩하는 것이 이상적입니다. 내부 구문은 간단한 for-each 루프를 만드는 배열이 될 것입니다. null이 아닌 값은 사소하고 분명합니다. 이 경우 Java 8 스트림은 매력적인 성가신 것입니다. 간단한 방법 / 루프 여야하는 것을 인라인하고 복잡하게 만들도록 유혹합니다.
Bill K

답변:


172

다음과 같이 할 수 있습니다.

String s = Stream.of(str1, str2, str3)
    .filter(Objects::nonNull)
    .findFirst()
    .orElse(str4);

16
이. 가지고있는 것이 아니라 필요한 것을 생각하십시오.
Thorbjørn Ravn Andersen

21
속도 오버 헤드는 얼마입니까? Stream 객체 생성, 4 개의 메서드 호출, 임시 배열 생성 ( {str1, str2, str3}) 은 Java 런타임이 최적화 할 수 있는 로컬 if또는 보다 훨씬 느립니다 ?:. 스트림 별 최적화 javac와 Java 런타임이 ?:있습니까? 그렇지 않은 경우 성능이 중요한 코드에서는이 솔루션을 권장하지 않습니다.
pts

16
@pts 이것은 ?:코드 보다 느릴 가능성이 매우 높으며 성능이 중요한 코드에서는 피해야한다고 확신합니다. 그러나 훨씬 더 읽기 쉽고 성능이 중요하지 않은 코드 IMO에서 권장해야합니다. 이는 코드의 99 % 이상을 생성합니다.
Aaron

7
@pts javac런타임이나 런타임 에 스트림 특정 최적화가 없습니다 . 이것은 모든 코드를 인라인하고 중복 작업을 제거하는 것과 같은 일반적인 최적화를 배제하지 않습니다. 원칙적으로 최종 결과는 일반 조건식만큼 효율적일 수 있지만 런타임이 가장 인기있는 코드 경로에만 필요한 노력을 소비하므로 거기에 도달 할 가능성은 거의 없습니다.
Holger

22
훌륭한 솔루션! 가독성을 조금 높이기 str4위해 매개 변수 를 추가 하고 결국 Stream.of(...)사용 하는 것이 좋습니다 orElse(null).
danielp

73

삼항 조건 연산자는 어떻습니까?

String s = 
    str1 != null ? str1 : 
    str2 != null ? str2 : 
    str3 != null ? str3 : str4
;

50
실제로 그는 "Java8에서이를 수행하는 가장 좋은 방법"을 찾고 있습니다. 이 접근 방식은 Java8에서 사용할 수 있으므로 OP가 "최고"라는 의미와 더 나은 결정을 내리는 근거에 따라 달라집니다.
Stultuske

17
나는 보통 중첩 된 삼진을 싫어하지만 이것은 꽤 깨끗해 보인다.
JollyJoker

11
이것은 중첩 된 삼항 연산자를 매일 읽지 않는 사람에게는 파싱하는 데 큰 고통입니다.
Cubic

2
@Cubic : 파싱하기 어렵다고 생각되면 // take first non-null of str1..3. 그것이 무엇을하는지 알게되면, 그 방법을 쉽게 알 수 있습니다.
Peter Cordes 2019

4
@Cubic 그러나 이것은 단순한 반복 패턴입니다. 2 행을 파싱하면 포함 된 케이스 수에 관계없이 전체를 파싱했습니다. 작업을 마치면 10 가지 다른 사례의 유사한 선택을 간단하고 비교적 간단한 방식으로 표현하는 방법을 배웠습니다. 다음에 ?:사다리를 보게되면 그것이 무엇을하는지 알게 될 것입니다. (Btw, 기능적 프로그래머는 이것을 (cond ...)절로 알고 있습니다 . 항상 가드 다음에 가드가 참일 때 사용하기에 적절한 값이
따릅니다

35

루프를 사용할 수도 있습니다.

String[] strings = {str1, str2, str3, str4};
for(String str : strings) {
    s = str;
    if(s != null) break;
}

27

현재 답변은 좋지만 실제로 유틸리티 메서드에 넣어야합니다.

public static Optional<String> firstNonNull(String... strings) {
    return Arrays.stream(strings)
            .filter(Objects::nonNull)
            .findFirst();
}

이 방법은 Util수년간 내 수업에 있었으며 코드를 훨씬 더 깔끔하게 만듭니다.

String s = firstNonNull(str1, str2, str3).orElse(str4);

일반화 할 수도 있습니다.

@SafeVarargs
public static <T> Optional<T> firstNonNull(T... objects) {
    return Arrays.stream(objects)
            .filter(Objects::nonNull)
            .findFirst();
}

// Use
Student student = firstNonNull(student1, student2, student3).orElseGet(Student::new);

10
FWIW, SQL에서는이 함수를 coalesce 라고 하므로 내 코드에서도이를 호출합니다. 그것이 당신에게 효과가 있는지 여부는 당신이 SQL을 얼마나 좋아하는지에 달려 있습니다.
Tom Anderson

5
유틸리티 메서드에 넣으려면 효율적으로 만드는 것이 좋습니다.
Todd Sewell

1
@ToddSewell, 무슨 뜻이야?
Gustavo Silva

5
@GustavoSilva Todd는 아마도 이것이 내 유틸리티 방법이기 때문에 Arrays.stream()a for및 a 를 사용 하여 동일한 작업을 수행 할 수있을 때 != null더 효율적이라는 의미가 없습니다. 그리고 Todd가 옳을 것입니다. 그러나이 방법을 코딩 할 때 OP와 마찬가지로 Java 8 기능을 사용하여이를 수행하는 방법을 찾고있었습니다.
walen

4
@GustavoSilva 예, 기본적으로 그게 다입니다. 만약 여러분이 이것이 유틸리티 함수라는 것을 넣으려면 깨끗한 코드에 대한 우려는 더 이상 중요하지 않으므로 더 빠른 버전을 사용하는 것이 좋습니다.
Todd Sewell

13

나는 다음과 같은 도우미 기능을 사용합니다.

T firstNonNull<T>(T v0, T... vs) {
  if(v0 != null)
    return v0;
  for(T x : vs) {
    if (x != null) 
      return x;
  }
  return null;
}

그런 다음 이러한 종류의 코드를 다음과 같이 작성할 수 있습니다.

String s = firstNonNull(str1, str2, str3, str4);

1
추가 v0매개 변수가 필요한 이유는 무엇 입니까?
tobias_k

1
@tobias_k varargs를 사용할 때 추가 매개 변수는 Java에서 0 개 이상의 인수가 아닌 1 개 이상의 인수를 요구하는 관용적 방법입니다. (Effective Java, Ed. 3의 53 번 항목을 참조 min하십시오. 여기에서는 이것이 적절하다고 생각하지 않습니다.)
Nick

3
@Nick 예, 많은 것을 짐작했지만 기능은 그것 없이도 잘 작동합니다 (실제로 정확히 동일하게 작동합니다).
tobias_k

1
추가 첫 번째 인수를 전달하는 주요 이유 중 하나는 배열을 전달할 때 동작에 대해 명시하기위한 것입니다. 이 경우 firstNonNull(arr)null이 아니면 arr을 반환 하고 싶습니다 . 그럴 경우 firstNonNull(T... vs)대신 arr에서 null이 아닌 첫 번째 항목을 반환합니다.
Michael Anderson

4

원하는만큼 많은 요소에 적용 할 수있는 솔루션은 다음과 같습니다.

Stream.of(str1, str2, str3, str4)
      .filter(Object::nonNull)
      .findFirst()
      .orElseThrow(IllegalArgumentException::new)

아래와 같은 솔루션을 상상할 수 있지만 첫 번째 솔루션 non nullity은 모든 요소를 보장 합니다.

Stream.of(str1, str2, str3).....orElse(str4)

6
OrElse라는 str4것이 Ɒ이 실제로 널 (null) 인
나만

1
@nullpointer Or orElseNull, str4값이 null 인 경우 동일합니다 .
tobias_k

3

또한 모든 문자열을 String 배열로 묶은 다음 for 루프를 수행하여 할당 된 루프를 확인하고 중단 할 수 있습니다. s1, s2, s3, s4가 모두 문자열이라고 가정합니다.

String[] arrayOfStrings = {s1, s2, s3};


s = s4;

for (String value : arrayOfStrings) {
    if (value != null) { 
        s = value;
        break;
    }
}

할당되지 않은 경우 기본적으로 s4에 대한 조건을 던지도록 편집되었습니다.


널 (NULL) 인 경우에도 결국 할당되어야하는 생략 s4(또는 str4)이 있습니다 s.
displayName

3

방법 기반 및 간단합니다.

String getNonNull(String def, String ...strings) {
    for(int i=0; i<strings.length; i++)
        if(strings[i] != null)
             return s[i];
    return def;
}

다음과 같이 사용하십시오.

String s = getNonNull(str4, str1, str2, str3);

배열로하는 것은 간단하고 예쁘게 보입니다.


0

Apache Commons Lang 3을 사용하는 경우 다음과 같이 작성할 수 있습니다.

String s = ObjectUtils.firstNonNull(str1, str2, str3, str4);

ObjectUtils.firstNonNull(T...)답변 에서의 사용을 가져 왔습니다 . 관련 질문 에서 다른 접근 방식도 제시되었습니다 .

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