Beberapa pemeriksaan nol di Java 8

99

Saya memiliki kode di bawah ini yang agak jelek untuk beberapa pemeriksaan nol.

String s = null;

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

Jadi saya mencoba menggunakan Optional.ofNullableseperti di bawah ini, tetapi masih sulit untuk dipahami jika seseorang membaca kode saya. apa pendekatan terbaik untuk melakukannya di Java 8.

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

Di Java 9, kita bisa menggunakan Optional.ofNullabledengan OR, Tapi di Java8 apakah ada pendekatan lain?

pecinta
sumber
4
orSintaks Java9 String s = Optional.ofNullable(str1) .or(() -> Optional.ofNullable(str2)) .or(() -> Optional.ofNullable(str3)) .orElse(str4);terlihat tidak sebaik yang Stream.ofsaya lakukan sya.
Naman
2
@ OleV.V. Tak ada salahnya, OP sudah menyadarinya dan sedang mencari sesuatu yang spesifik untuk Java-8.
Naman
3
Saya tahu pengguna meminta solusi khusus Java-8, tetapi pada catatan umum, saya akan memilihStringUtils.firstNonBlank()
Mohamed Anees A
3
Masalahnya, Java 8 / stream bukanlah solusi terbaik untuk ini. Kode itu benar-benar berbau seperti refactor yang berurutan tetapi tanpa konteks yang lebih banyak, sangat sulit untuk mengatakannya. Sebagai permulaan - mengapa tiga objek yang mungkin sangat terkait tidak belum ada dalam sebuah koleksi?
Bill K
2
@MohamedAneesA akan memberikan jawaban terbaik (sebagai komentar) tetapi tidak menentukan sumber StringUtils dalam kasus ini. Bagaimanapun, jika Anda HARUS memiliki ini sebagai sekumpulan string terpisah, mengkodekannya sebagai metode vargs seperti "firstNonBlank" sangat ideal, sintaks di dalamnya akan menjadi larik yang membuat sederhana untuk-setiap loop dengan pengembalian menemukan a nilai non-null sepele dan jelas. Dalam hal ini, aliran java 8 adalah gangguan yang menarik. mereka menggoda Anda untuk menyebariskan dan memperumit sesuatu yang seharusnya menjadi metode / putaran sederhana.
Bill K

Jawaban:

172

Anda dapat melakukannya seperti ini:

String s = Stream.of(str1, str2, str3)
    .filter(Objects::nonNull)
    .findFirst()
    .orElse(str4);
Ravindra Ranwala
sumber
16
Ini. Pikirkan apa yang Anda butuhkan, bukan apa yang Anda miliki.
Thorbjørn Ravn Andersen
21
Berapa kecepatan overhead? Membuat objek Stream, memanggil 4 metode, membuat array sementara ( {str1, str2, str3}) terlihat jauh lebih lambat daripada lokal ifatau ?:, yang dapat dioptimalkan oleh runtime Java. Apakah ada beberapa pengoptimalan khusus Stream javacdan runtime Java yang membuatnya secepat itu ?:? Jika tidak, saya tidak akan merekomendasikan solusi ini dalam kode kinerja-kritis.
poin
16
@pts ini sangat mungkin lebih lambat daripada ?:kode dan saya yakin Anda harus menghindarinya dalam kode kinerja-kritis. Namun itu jauh lebih mudah dibaca dan Anda harus merekomendasikannya dalam kode IMO non-kinerja-kritis, yang saya yakin membuat lebih dari 99% kode.
Aaron
7
@pts Tidak ada pengoptimalan khusus Aliran, baik dalam javacmaupun dalam waktu proses. Ini tidak menghalangi pengoptimalan umum, seperti menyebariskan semua kode, diikuti dengan menghilangkan operasi yang berlebihan. Pada prinsipnya, hasil akhirnya bisa seefisien ekspresi kondisional biasa, namun, agak tidak mungkin itu pernah sampai di sana, karena runtime akan menghabiskan upaya yang diperlukan hanya pada jalur kode terpanas.
Holger
22
Solusi bagus! Untuk sedikit meningkatkan keterbacaan saya akan menyarankan untuk menambahkan str4parameter Stream.of(...)dan penggunaan orElse(null)pada akhirnya.
danielp
73

Bagaimana dengan operator kondisional terner?

String s = 
    str1 != null ? str1 : 
    str2 != null ? str2 : 
    str3 != null ? str3 : str4
;
Eran
sumber
50
sebenarnya, dia mencari "pendekatan terbaik untuk melakukan ini di Java8". Pendekatan ini dapat digunakan di Java8, jadi semuanya tergantung pada apa yang dimaksud OP dengan "yang terbaik", dan atas dasar apa ia mendasarkan keputusan tentang apa yang lebih baik.
Stultuske
17
Saya biasanya tidak suka terner bersarang, tetapi ini terlihat cukup bersih.
JollyJoker
11
Ini adalah kesulitan yang sangat besar untuk diurai bagi siapa saja yang tidak membaca operator terner bersarang setiap hari.
Kubik
2
@Cubic: Jika Anda merasa sulit untuk mengurai, tulis komentar seperti // take first non-null of str1..3. Setelah Anda mengetahui fungsinya, akan mudah untuk melihat caranya.
Peter Cordes
4
@Cubic Namun, ini adalah pola berulang sederhana . Setelah Anda mengurai baris 2, Anda telah mengurai semuanya, tidak peduli berapa banyak kasus yang disertakan. Dan setelah Anda selesai, Anda telah belajar untuk mengungkapkan pilihan serupa dari sepuluh kasus berbeda secara singkat dan dengan cara yang relatif sederhana. Lain kali Anda akan melihat ?:tangga, Anda akan tahu fungsinya. (Btw, programmer fungsional tahu ini sebagai (cond ...)klausul: Itu selalu penjaga diikuti dengan nilai yang sesuai untuk digunakan ketika penjaga itu benar.)
cmaster - mengembalikan monica
35

Anda juga bisa menggunakan loop:

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

Jawaban saat ini bagus tetapi Anda benar-benar harus memasukkannya ke dalam metode utilitas:

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

Metode itu telah ada di Utilkelas saya selama bertahun-tahun, membuat kode jauh lebih bersih:

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

Anda bahkan dapat membuatnya menjadi generik:

@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
sumber
10
FWIW, di SQL, fungsi ini disebut coalesce , jadi saya menyebutnya juga di kode saya. Apakah itu berhasil untuk Anda tergantung seberapa besar Anda menyukai SQL, sungguh.
Tom Anderson
5
Jika Anda akan memasukkannya ke dalam metode utilitas, Anda mungkin juga membuatnya efisien.
Todd Sewell
1
@ToddSewell, apa maksudmu?
Gustavo Silva
5
@GustavoSilva Todd mungkin berarti bahwa, ini adalah metode utilitas saya, tidak ada gunanya menggunakan Arrays.stream()ketika saya dapat melakukan hal yang sama dengan a fordan a != null, yang lebih efisien. Dan Todd benar. Namun, ketika saya mengkodekan metode ini, saya mencari cara untuk melakukan ini menggunakan fitur Java 8, seperti OP, jadi begitulah.
Walen
4
@GustavoSilva Ya pada dasarnya itu: jika Anda akan menggunakan ini adalah fungsi util, kekhawatiran tentang kode bersih tidak lagi penting, jadi Anda sebaiknya menggunakan versi yang lebih cepat.
Todd Sewell
13

Saya menggunakan fungsi pembantu, seperti

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

Kemudian kode semacam ini dapat ditulis sebagai

String s = firstNonNull(str1, str2, str3, str4);
Michael Anderson
sumber
1
Mengapa v0parameter ekstra ?
tobias_k
1
@tobias_k Parameter ekstra saat menggunakan varargs adalah cara idiomatik di Java yang membutuhkan 1 atau lebih argumen daripada 0 atau lebih. (Lihat item 53 dari Effective Java, Ed. 3., yang digunakan minsebagai contoh.) Saya kurang yakin bahwa ini tepat di sini.
Nick
3
@Nick Ya, saya sudah menebaknya, tetapi fungsinya akan bekerja dengan baik (sebenarnya berperilaku persis sama) tanpanya.
tobias_k
1
Salah satu alasan utama untuk meneruskan argumen ekstra pertama adalah bersikap eksplisit tentang perilaku saat meneruskan array. Dalam hal ini, saya ingin firstNonNull(arr)mengembalikan arr jika bukan null. Jika itu adalah firstNonNull(T... vs)sebaliknya akan mengembalikan entri non-null pertama di arr.
Michael Anderson
4

Solusi yang dapat diterapkan ke sebanyak mungkin elemen yang Anda inginkan adalah:

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

Anda dapat membayangkan solusi seperti di bawah ini, tetapi yang pertama memastikan non nullitysemua elemen

Stream.of(str1, str2, str3).....orElse(str4)
azro
sumber
6
orElse str4meskipun itu menjadi nol sebenarnya
Naman
1
@nullpointer Atau orElseNull, yang jumlahnya sama jika str4null.
tobias_k
3

Anda juga dapat menggabungkan semua String ke dalam array String lalu melakukan perulangan for untuk memeriksa dan memutuskan dari perulangan setelah ditetapkan. Dengan asumsi s1, s2, s3, s4 semuanya adalah String.

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


s = s4;

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

Diedit untuk memasukkan kondisi default ke s4 jika tidak ada yang ditetapkan.

danielctw.dll
sumber
Anda telah menghilangkan s4(atau str4), yang seharusnya ditetapkan spada akhirnya, bahkan jika nilainya null.
displayName
3

Berbasis metode dan sederhana.

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

Dan gunakan sebagai:

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

Ini mudah dilakukan dengan array dan terlihat cantik.

Madhusoodan P
sumber
0

Jika Anda menggunakan Apache Commons Lang 3 maka dapat ditulis seperti ini:

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

Penggunaan ObjectUtils.firstNonNull(T...)diambil dari jawaban ini . Pendekatan yang berbeda juga disajikan dalam pertanyaan terkait .

lczapski.dll
sumber