104

I have the below code which is bit ugly for multiple null checks.

String s = null;

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

So I tried using Optional.ofNullable like below, but its still difficult to understand if someone reads my code. what is the best approach to do that in Java 8.

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

In Java 9, we can use Optional.ofNullablewith OR, But in Java8 is there any other approach ?

Ravindra Ranwala
  • 20,744
  • 6
  • 45
  • 63
sparker
  • 1,666
  • 4
  • 21
  • 35
  • 4
    Java9 `or` syntax `String s = Optional.ofNullable(str1) .or(() -> Optional.ofNullable(str2)) .or(() -> Optional.ofNullable(str3)) .orElse(str4);` looks not as good as the `Stream.of` I would sya. – Naman Feb 21 '19 at 06:55
  • `Optional.ofNullable` is in Java-8 only, i guess. – Vishwa Ratna Feb 21 '19 at 06:58
  • This was what the elvis operator should do easily. I think it is a good thing it wasn't introduced as it makes it easier to work with null values which I think is the wrong way to go. – Thorbjørn Ravn Andersen Feb 21 '19 at 08:22
  • 3
    I know the user is asking for Java-8 specific solution, but on a general note, I would go with `StringUtils.firstNonBlank()` – Mohamed Anees A Feb 21 '19 at 12:09
  • 3
    Problem is, Java 8/streams isn't the best solution for this. That code really smells like a refactor is in order but without more context it's really hard to tell. For starters--why aren't three objects that are probably so closely related not already in a collection? – Bill K Feb 21 '19 at 17:49
  • 2
    @MohamedAneesA would have provided the best answer (as a comment) but didn't specify the source of StringUtils in this case. At any rate, if you HAVE to have these as a bunch of separate strings, coding it as a vargs method like "firstNonBlank" is ideal, the syntax inside will be an array making a simple for-each loop with a return on finding a non-null value trivial and obvious. In this case, java 8 streams are an attractive nuisance. they tempt you to inline and complicate something that should be a simple method/loop. – Bill K Feb 21 '19 at 17:55
  • 1
    https://stackoverflow.com/q/2768054/31782 – n0rd Feb 21 '19 at 20:48
  • Ok, I just have to ask... how did this post get so much activity and so many upvotes, as well as upvoted answers, in a single day? – 0xCursor Feb 23 '19 at 01:10

10 Answers10

181

You may do it like so:

String s = Stream.of(str1, str2, str3)
    .filter(Objects::nonNull)
    .findFirst()
    .orElse(str4);
Sulthan
  • 128,090
  • 22
  • 218
  • 270
Ravindra Ranwala
  • 20,744
  • 6
  • 45
  • 63
  • 17
    This. Think in what you need, not what you have. – Thorbjørn Ravn Andersen Feb 21 '19 at 08:23
  • 22
    How much is the speed overhead? Creating Stream objects, calling 4 methods, creating temporary arrays (`{str1, str2, str3}`) look like much slower than a local `if` or `?:`, which the Java runtime can optimize. Is there some Stream-specific optimization in `javac` and the Java runtime which makes it as fast as `?:`? If not, I won't recommend this solution in performance-critical code. – pts Feb 21 '19 at 09:58
  • 17
    @pts this is very likely to be slower than `?:` code and I'm sure you should avoid it in performance-critical code. It's however much more readable and you should recommend it in non-performance-critical code IMO, which I'm sure makes more than 99% of code. – Aaron Feb 21 '19 at 10:39
  • 7
    @pts There are no Stream specific optimizations, neither in `javac` nor in the runtime. This does not preclude the general optimizations, like inlining all of the code, followed by eliminating redundant operations. In principle, the end result could be as efficient as the plain conditional expressions, however, it’s rather unlikely that it ever gets there, as the runtime would spend the necessary effort only on the hottest code paths. – Holger Feb 21 '19 at 10:40
  • 4
    @pts The creation and reclamation of small objects whose constructors do little explicit work is cheap, especially on modern JVM implementations. Creating additional objects to enhance the clarity, simplicity, or power of a program is generally a good thing. There's a deep truth about optimization: it is easy to do more harm than good, especially if you optimize prematurely. So unless your application is performance stringent, you can use this approach with out any hesitation. – Ravindra Ranwala Feb 21 '19 at 16:11
  • 24
    Great solution! To increase readability a little bit I would suggest to add `str4` to the parameters of `Stream.of(...)` and use `orElse(null)` in the end. – danielp Feb 21 '19 at 23:12
  • here findFirst() and findAny() will return the same, what if we have more than one not null; – Vishwa Ratna Feb 25 '19 at 10:21
  • I guess I've been programming before jdk 8 came out for too long, but this doesn't seem any more readable to me. It's just different. It does have an advantage that the number of lines is (more or less) fixed regardless of the number of items to check. – Alvin Thompson Feb 27 '19 at 17:29
  • 1
    Pretty nice answer. Thanks for sharing – Chauyan Mar 18 '19 at 18:01
74

How about ternary conditional operator?

String s = 
    str1 != null ? str1 : 
    str2 != null ? str2 : 
    str3 != null ? str3 : str4
;
candied_orange
  • 7,036
  • 2
  • 28
  • 62
Eran
  • 387,369
  • 54
  • 702
  • 768
  • 50
    actually, he's looking for "the best approach to do this in Java8". This approach can be used in Java8, so it all just depends on what the OP means by "the best", and on which grounds he bases the decision of what is better on. – Stultuske Feb 21 '19 at 07:13
  • 18
    I usually dislike nested ternaries, but this looks pretty clean. – JollyJoker Feb 21 '19 at 08:59
  • 11
    This is a huge pain to parse for anyone who isn't reading nested ternary operators every day. – Cubic Feb 21 '19 at 15:46
  • 2
    @Cubic: If you think it's hard to parse, write a comment like `// take first non-null of str1..3`. Once you know what it does, it becomes easy to see how. – Peter Cordes Feb 22 '19 at 03:36
  • @displayName I prefer to never use auto-formatting. It's impossible to teach an editor how to align things like this, much better to do it yourself. – Bill K Feb 22 '19 at 16:50
  • 4
    @Cubic Yet, this is a **simple** repeated pattern. Once you parsed line 2, you've parsed the entire thing, no matter how many cases are included. And once you are done, you've learned to express a similar selection of ten different cases in a brief, and relatively simple manner. The next time you'll see a `?:` ladder, you'll know what it does. (Btw, functional programmers know this as `(cond ...)` clauses: It's always a guard followed by the appropriate value to use when the guard is true.) – cmaster - reinstate monica Feb 22 '19 at 17:18
35

You can also use a loop:

String[] strings = {str1, str2, str3, str4};
for(String str : strings) {
    s = str;
    if(s != null) break;
}
ernest_k
  • 44,416
  • 5
  • 53
  • 99
27

Current answers are nice but you really should put that in a utility method:

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

That method has been in my Util class for years, makes code much cleaner:

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

You can even make it generic:

@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);
walen
  • 7,103
  • 2
  • 37
  • 58
  • 10
    FWIW, in SQL, this function is called [coalesce](https://www.postgresql.org/docs/11/functions-conditional.html#FUNCTIONS-COALESCE-NVL-IFNULL), so i call it that in my code too. Whether that works for you depends how much you like SQL, really. – Tom Anderson Feb 21 '19 at 14:48
  • 7
    If you're going to put it in a utility method you might as well make it efficient. – Todd Sewell Feb 21 '19 at 19:55
  • 5
    @GustavoSilva Todd probably means that, this being a utility method of mine, there's no point in using `Arrays.stream()` when I can do the same with a `for` and a `!= null`, which is more efficient. And Todd would be right. However, when I coded this method I was looking for a way of doing this using Java 8 features, just like OP, so there's that. – walen Feb 22 '19 at 12:23
  • 4
    @GustavoSilva Yeah that's basically it: if you're going to be putting this is a util function concerns about clean code aren't that important anymore, and so you might as well use a faster version. – Todd Sewell Feb 22 '19 at 17:05
13

I use a helper function, something like

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

Then this kind of code can be written as

String s = firstNonNull(str1, str2, str3, str4);
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • 1
    Why the extra `v0` parameter? – tobias_k Feb 23 '19 at 10:08
  • 2
    @tobias_k The extra parameter when using varargs is the idiomatic way in Java to require 1 or more arguments rather than 0 or more. (See item 53 of Effective Java, Ed. 3., which uses `min` as an example.) I am less convinced that this is appropriate here. – Nick Feb 23 '19 at 15:49
  • 3
    @Nick Yes, I guessed as much, but the function would work just as well (in fact behave exactly the same) without it. – tobias_k Feb 23 '19 at 16:54
  • 2
    One of the key reasons for passing an extra first argument is to be explicit about the behaviour when passed an array. In this case, I want `firstNonNull(arr)` to return arr if it is not null. If it was `firstNonNull(T... vs)` it would instead return the first non-null entry in arr. – Michael Anderson Feb 24 '19 at 12:09
4

A solution which can be applied to as many element as you want can be :

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

You could imagine a solution like below, but the first one ensures non nullity for all of the elements

Stream.of(str1, str2, str3).....orElse(str4)
azro
  • 53,056
  • 7
  • 34
  • 70
3

You can also lump up all the Strings into an array of String then do a for loop to check and break from the loop once it's assigned. Assuming s1, s2, s3, s4 are all Strings.

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


s = s4;

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

Edited to throw in condition for default to s4 if none is assigned.

danielctw
  • 83
  • 6
2

Method based and simple.

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

And use it as:

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

It's simple to do with arrays and looks pretty.

Madhusoodan P
  • 681
  • 9
  • 20
2

If you use Apache Commons Lang 3 then it can be written like this:

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

Use of ObjectUtils.firstNonNull(T...) was taken from this answer. Different approaches were also presented in related question.

lczapski
  • 4,026
  • 3
  • 16
  • 32
0

Using of a for loop will be the most suitable solution, as all beginner and experienced developers have enough knowledge of Loops. It's quite simple, first make an array, and then check the entries one by one. If any nonNull string found then stop the loop, and proceed to the results.

String[] myData = {s1, s2, s3, s4};
String s = null;

for (String temp : myData) {
    if (temp != null) { 
        s = temp;
        break;
    }
}

Now you can track the nonNull value of the string, or can check if all strings were null, by using the below code.

if(s != null)
    System.out.println(s);
else
    System.out.println("All Strings are null");
Numan Gillani
  • 489
  • 9
  • 19