Как получить первое ненулевое значение в Java? - PullRequest
132 голосов
/ 04 мая 2010

Существует ли Java-эквивалент функции COALESCE в SQL? То есть есть ли способ вернуть первое ненулевое значение нескольких переменных?

, например

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

Я хочу каким-то образом иметь оператор, который будет возвращать первое ненулевое значение a, b и c - в этом случае он вернет b или 4.4. (Что-то вроде метода sql - return COALESCE(a,b,c)). Я знаю, что могу сделать это явно с помощью чего-то вроде:

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

Но мне было интересно, есть ли какая-нибудь встроенная, принятая функция для достижения этой цели.

Ответы [ 12 ]

154 голосов
/ 11 сентября 2013
99 голосов
/ 04 мая 2010

Нет, нет.

Ближайшее, что вы можете получить:

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 ...
}
53 голосов
/ 18 июля 2011

Если нужно проверить только две переменные и вы используете Guava, вы можете использовать MoreObjects.firstNonNull (T first, T second) .

41 голосов
/ 03 февраля 2015

Если есть только две ссылки для тестирования и вы используете Java 8, вы можете использовать

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

Если вы импортируете static Необязательный, выражение не так уж плохо.

К сожалению, ваш случай с «несколькими переменными» невозможен при использовании Optional-метода. Вместо этого вы можете использовать:

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
24 голосов
/ 04 мая 2010

Исходя из ответа 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);
}
8 голосов
/ 19 февраля 2012

Эта ситуация требует некоторого препроцессора. Потому что, если вы напишите функцию (статический метод), которая выбирает первое ненулевое значение, она оценивает все элементы. Это проблема, если некоторые элементы являются вызовами методов (могут быть дорогостоящие вызовы методов). И эти методы вызываются, даже если любой элемент перед ними не равен нулю.

Некоторые функции, подобные этой

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

следует использовать, но перед компиляцией в байт-код должен быть препроцессор, который находит использование этой «функции объединения» и заменяет ее конструкцией, подобной

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

Обновление 2014-09-02:

Благодаря Java 8 и Lambdas появилась возможность объединиться в Java! Включая критическую особенность: определенные выражения оцениваются только при необходимости - если ранее одно из них не является нулевым, то следующие не оцениваются (методы не вызываются, вычисления или операции с диском / сетью не выполняются).

Я написал статью об этом Java 8: coalesce - hledáme neNULLové hodnoty - (написано на чешском языке, но я надеюсь, что примеры кода понятны всем).

5 голосов
/ 25 июля 2013

С Guava вы можете сделать:

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

, который не бросает NPE, если a и b равны null.

РЕДАКТИРОВАТЬ: я был не прав, он бросает NPE. Правильный путь, прокомментированный Michal Čizmazia :

Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull();
4 голосов
/ 18 марта 2015

Просто для полноты, случай "нескольких переменных" действительно возможен, хотя и не элегантен. Например, для переменных o, p и q:

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() или просто effectiveAge, если мы довольны Supplier<Integer>.

ИМХО, с Java 8 мы увидим все больше и больше кода, структурированного подобным образом, поскольку он чрезвычайно понятен и эффективен одновременно, особенно в более сложных случаях.

Я пропускаю класс Lazy<T>, который вызывает Supplier<T> только один раз, но лениво, а также согласованность в определении Optional<T> (то есть Optional<T> - Optional<T> операторов или даже Supplier<Optional<T>>).

3 голосов
/ 10 января 2018

Вы можете попробовать это:

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

На основании этого ответа

1 голос
/ 20 апреля 2018

Как насчет использования поставщиков, если вы хотите избежать оценки какого-либо дорогостоящего метода?

Как это:

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);
}
...