Java에서 null이 아닌 첫 번째 값을 얻는 방법은 무엇입니까?


154

Java와 동등한 SQL이 있습니까? COALESCE 기능이 있습니까? 즉, 여러 변수의 첫 번째 null이 아닌 값을 반환하는 방법이 있습니까?

예 :

Double a = null;
Double b = 4.4;
Double c = null;

어떻게 든의 첫 번째 null이 아닌 값을 반환하는 문을 갖고 싶어 a, b그리고 c-이 경우, 반환 b, 또는 4.4. (sql 메소드와 같은 것-return COALESCE(a,b,c)). 나는 다음과 같이 명시 적으로 할 수 있음을 알고 있습니다.

return a != null ? a : (b != null ? b : c)

그러나이 작업을 수행하기위한 기본 제공 기능이 있는지 궁금했습니다.


3
'b'에 원하는 대답이 있으면 일반적으로 'c'를 계산하지 않으므로 이와 같은 함수가 필요하지 않습니다. 즉, 가능한 답변 목록 만 작성하여 답변을 유지하지는 않습니다.
피터로 레이

주의 사항 : COALESCE의 모든 RDBMS 단락이 아닙니다. 오라클은 최근에야이를 시작했습니다.
Adam Gent

3
@ BrainSlugs83 정말요? 자바?
Dmitry Ginzburg 12

답변:


108

아닙니다.

당신이 얻을 수있는 가장 가까운 것은 :

public static <T> T coalesce(T ...items) {
    for(T i : items) if(i != null) return i;
    return null;
}

효율적인 이유로 일반적인 경우를 다음과 같이 처리 할 수 ​​있습니다.

public static <T> T coalesce(T a, T b) {
    return a == null ? b : a;
}
public static <T> T coalesce(T a, T b, T c) {
    return a != null ? a : (b != null ? b : c);
}
public static <T> T coalesce(T a, T b, T c, T d) {
    return ...
}

3
위에서 언급 한 효율성 이유는 메소드의 var arg 버전을 호출 할 때마다 배열 할당이 발생하기 때문입니다. 이것은 많은 양의 아이템에 낭비 될 수 있습니다.
les2

멋있는. 감사. 이 경우에는 사용되어야 할 유일한 시간이고 사용자 정의 방법이 과도 할 수
있으므로이

8
나는 여전히 코드에 "무서워 보이는"조건부 블록을 남기지 않고 개인 헬퍼 메소드로 꺼내려고한다. 그렇게하면 다시 사용해야 할 경우 IDE의 리팩토링 도구를 사용하여 메소드를 유틸리티 클래스로 이동할 수 있습니다. 명명 된 방법을 사용하면 코드의 의도를 문서화하는 데 도움이됩니다. 이는 항상 좋은 것입니다. IMO. (그리고 var-args가 아닌 버전의 오버 헤드는 거의 측정 할 수 없을 것입니다.)
les2

10
조심 : 인을 coalesce(a, b), 경우 b복잡한 표현과 a아닌 null, b여전히 평가된다. ? : 조건부 연산자의 경우에는 해당되지 않습니다. 이 답변을 참조하십시오 .
Pang

이를 위해서는 성능상의 이유로 무의미한 통합을 요청하기 전에 모든 인수를 미리 계산해야합니다.
Ivan G.


59

확인할 두 개의 변수 만 있고 구아바를 사용하는 경우 MoreObjects.firstNonNull (T first, T second)를 사용할 수 있습니다 .


49
Objects.firstNonNull은 두 개의 인수 만 취합니다. 구아바에는 해당하는 varargs가 없습니다. 또한 두 인수가 모두 null 인 경우 NullPointerException이 발생합니다. 바람직하지 않을 수도 있습니다.

2
좋은 의견이에요, 제이크 이 NullPointerException은 종종 Objects.firstNonNull 사용을 제한합니다. 그러나 null을 피하는 것은 Guava의 접근 방식입니다.
Anton Shchastnyi

4
이 방법은 더 이상 사용되지 않으며 권장되는 대안은 MoreObjects.firstNonNull
davidwebster48

1
NPE가 바람직하지 않은 경우이 답변
OrangeDog

51

테스트 할 두 개의 참조 만 있고 Java 8을 사용하는 경우

Object o = null;
Object p = "p";
Object r = Optional.ofNullable( o ).orElse( p );
System.out.println( r );   // p

static 선택적을 가져 오는 경우 표현식이 나쁘지 않습니다.

불행히도 "여러 변수"의 경우는 선택적 방법으로는 불가능합니다. 대신 다음을 사용할 수 있습니다.

Object o = null;
Object p = null;
Object q = "p";

Optional<Object> r = Stream.of( o, p, q ).filter( Objects::nonNull ).findFirst();
System.out.println( r.orElse(null) );   // p

23

LES2의 답변에 따라 오버로드 된 함수를 호출하여 효율적인 버전에서 일부 반복을 제거 할 수 있습니다.

public static <T> T coalesce(T a, T b) {
    return a != null ? a : b;
}
public static <T> T coalesce(T a, T b, T c) {
    return a != null ? a : coalesce(b,c);
}
public static <T> T coalesce(T a, T b, T c, T d) {
    return a != null ? a : coalesce(b,c,d);
}
public static <T> T coalesce(T a, T b, T c, T d, T e) {
    return a != null ? a : coalesce(b,c,d,e);
}

5
예쁜 +1 단순 루프에 비해 효율성 이점에 대해 잘 모르지만, 이런 식으로 작은 효율성을 없애려면 꽤 좋을 수도 있습니다.
Carl Manaster

3
이렇게하면 오버로드 된 변형을 작성하는 것이 훨씬 덜 고통스럽고 오류가 적습니다!
les2

2
효율적인 버전의 핵심은를 사용하여 어레이를 할당하는 메모리를 낭비하지 않는 것이 었습니다 varargs. 여기에서는 중첩 된 각 coalesce()호출 에 대해 스택 프레임을 만들어 메모리를 낭비하고 있습니다. 호출 coalesce(a, b, c, d, e)하면 계산할 스택 프레임이 3 개까지 생성됩니다.
누가

10

이 상황에서는 일부 전처리 기가 필요합니다. 널이 아닌 첫 번째 값을 선택하는 함수 (정적 메소드)를 작성하면 모든 항목을 평가하기 때문입니다. 일부 항목이 메소드 호출 인 경우 문제가됩니다 (시간이 오래 걸리는 메소드 호출 일 수 있음). 그리고이 메소드는 앞에있는 항목이 널이 아닌 경우에도 호출됩니다.

이 같은 기능

public static <T> T coalesce(T ...items) 

바이트 코드로 컴파일하기 전에이 "coalesce function"의 사용법을 찾고이를 다음과 같은 구성으로 대체하는 전처리 기가 있어야합니다.

a != null ? a : (b != null ? b : c)

2014-09-02 업데이트 :

Java 8 및 Lambdas 덕분에 Java에서 진정한 통합이 가능할 수 있습니다! 중요한 기능 포함 : 특정 표현식은 필요할 때만 평가됩니다. 이전 표현식이 널이 아닌 경우 다음 표현식은 평가되지 않습니다 (메소드가 호출되지 않거나 계산 또는 디스크 / 네트워크 조작이 수행되지 않음).

Java 8 에 대한 기사를 작성했습니다 – 통합 – hledáme neNULLové hodnoty – (체코 어로 작성되었지만 모든 사람들이 코드 예제를 이해할 수 있기를 바랍니다).


1
좋은 기사-영어로하는 것이 좋을 것입니다.
퀀텀

1
해당 블로그 페이지에는 Google 번역에서 작동하지 않는 내용이 있습니다. :-(
HairOfTheDog

5

구아바로 할 수있는 일 :

Optional.fromNullable(a).or(b);

NPE가 발생하면 모두하지 않는 a하고 b있습니다null .

편집 : 나는 틀렸다, 그것은 NPE를 던진다. Michal Čizmazia 가 논평 한 올바른 방법 은 다음과 같습니다.

Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull();

1
이봐 요,java.lang.NullPointerException: use Optional.orNull() instead of Optional.or(null)
Michal Čizmazia

1
이것은 속임수를한다 :Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull()
Michal Čizmazia

4

완전 함을 위해서, "여러 변수"사례는 실제로 가능하지만 전혀 우아하지는 않습니다. 예를 들어, 변수 o, pq:

Optional.ofNullable( o ).orElseGet(()-> Optional.ofNullable( p ).orElseGet(()-> q ) )

의 사용에 유의하시기 바랍니다 orElseGet()경우에 참석을하는 o, p그리고q 변수 만 표현 비싸거나 바람직하지 않은 부작용과 하나되지 않습니다를.

가장 일반적인 경우 coalesce(e[1],e[2],e[3],...,e[N])

coalesce-expression(i) ==  e[i]  when i = N
coalesce-expression(i) ==  Optional.ofNullable( e[i] ).orElseGet(()-> coalesce-expression(i+1) )  when i < N

이로 인해 표현식이 지나치게 길어질 수 있습니다. 그러나 우리가없는 세상으로 이동하려고한다면 null, v[i]아마도 Optional<String>단순히와는 대조적으로 이미 유형일 것입니다 String. 이 경우

result= o.orElse(p.orElse(q.get())) ;

또는 표현의 경우 :

result= o.orElseGet(()-> p.orElseGet(()-> q.get() ) ) ;

당신은 또한 기능-선언 스타일로 이동하는 경우 또한,, o, p, 및 q유형을 사용해야 Supplier<String>처럼 :

Supplier<String> q= ()-> q-expr ;
Supplier<String> p= ()-> Optional.ofNullable(p-expr).orElseGet( q ) ;
Supplier<String> o= ()-> Optional.ofNullable(o-expr).orElseGet( p ) ;

그리고 전체 coalesce가 단순히로 줄어 듭니다 o.get().

보다 구체적인 예를 들면 다음과 같습니다.

Supplier<Integer> hardcodedDefaultAge= ()-> 99 ;
Supplier<Integer> defaultAge= ()-> defaultAgeFromDatabase().orElseGet( hardcodedDefaultAge ) ;
Supplier<Integer> ageInStore= ()-> ageFromDatabase(memberId).orElseGet( defaultAge ) ;
Supplier<Integer> effectiveAge= ()-> ageFromInput().orElseGet( ageInStore ) ;

defaultAgeFromDatabase(), ageFromDatabase()그리고 ageFromInput()이미 반환 Optional<Integer>자연스럽게.

그리고 우리가 행복하다면 단순히 또는 coalesce됩니다 .effectiveAge.get()effectiveAgeSupplier<Integer>

IMHO, Java 8에서는 특히 더 복잡한 경우에 매우 자기 설명적이고 효율적이기 때문에 이와 같이 더 많은 코드가 이와 같이 구조화됩니다.

나는 단 한 번만 Lazy<T>호출 Supplier<T>하지만 게으르게 클래스 의 정의 Optional<T>(예 Optional<T>: Optional<T>연산자 또는 심지어 Supplier<Optional<T>>) 를 일관성있게 호출합니다 .


4

당신은 이것을 시도 할 수 있습니다 :

public static <T> T coalesce(T... t) {
    return Stream.of(t).filter(Objects::nonNull).findFirst().orElse(null);
}

응답을 바탕으로


3

값 비싼 방법을 평가하지 않으려는 경우 공급 업체를 사용하는 것은 어떻습니까?

이처럼 :

public static <T> T coalesce(Supplier<T>... items) {
for (Supplier<T> item : items) {
    T value = item.get();
    if (value != null) {
        return value;
    }
    return null;
}

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

Double amount = coalesce(order::firstAmount, order::secondAmount, order::thirdAmount)

두 개, 세 개 또는 네 개의 인수가있는 호출에 오버로드 된 메소드를 사용할 수도 있습니다.

또한 다음과 같은 스트림을 사용할 수도 있습니다.

public static <T> T coalesce2(Supplier<T>... s) {
    return Arrays.stream(s).map(Supplier::get).filter(Objects::nonNull).findFirst().orElse(null);
}

Supplier어쨌든 검사 할 경우 첫 번째 인수를 왜 랩핑 합니까? 균일 성을 위해?
Inego

0

어때요?

firstNonNull = FluentIterable.from(
    Lists.newArrayList( a, b, c, ... ) )
        .firstMatch( Predicates.notNull() )
            .or( someKnownNonNullDefault );

Java ArrayList는 편리하게 널 항목을 허용하며이 표현식은 고려할 오브젝트 수에 관계없이 일관됩니다. (이 형태에서 고려되는 모든 객체는 동일한 유형이어야합니다.)


-3
Object coalesce(Object... objects)
{
    for(Object o : object)
        if(o != null)
            return o;
    return null;
}

2
신 나는 제네릭을 싫어한다. 나는 당신의 말이 박쥐에서 바로 무엇을 의미하는지 보았습니다. 나는 그가 같은 일을하고 있다는 것을 알아 내기 위해 @ LES2를 두 번 봐야했다. 명확성을 위해 +1
Bill K

네, 제네릭이 갈 길입니다. 그러나 나는 그 복잡한 것에 익숙하지 않다.
Eric

10
제네릭을 배울 시간 :-). @ LES2의 예제와 Object 사이의 T 이외의 차이점은 거의 없습니다. 리턴 값을 다시 Double로 강제로 캐스팅하는 함수를 빌드하는 경우 -1입니다. 또한 모든 대문자로 Java 메소드의 이름을 지정하기 위해 SQL에는 적합하지만 Java에서는 스타일이 좋지 않습니다.
Avi

1
나는 모든 대문자가 나쁜 습관이라는 것을 알고 있습니다. 방금 OP에 요청한 이름으로 함수를 작성하는 방법을 보여주었습니다. 합의한 캐스트 Double는 이상 과 는 거리가 멀다. 정적 함수에 유형 매개 변수가 주어질 수 있다는 것을 알지 못했습니다. 수업 일 뿐이라고 생각했습니다.
Eric
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.