Nhiều kiểm tra rỗng trong Java 8


99

Tôi có đoạn mã dưới đây hơi xấu cho nhiều lần kiểm tra rỗng.

String s = null;

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

Vì vậy, tôi đã thử sử dụng Optional.ofNullablenhư bên dưới, nhưng vẫn khó hiểu nếu ai đó đọc mã của tôi. cách tốt nhất để làm điều đó trong Java 8 là gì.

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

Trong Java 9, chúng ta có thể sử dụng Optional.ofNullablevới OR, Nhưng trong Java8 có cách tiếp cận nào khác không?


4
Java9 orcú pháp String s = Optional.ofNullable(str1) .or(() -> Optional.ofNullable(str2)) .or(() -> Optional.ofNullable(str3)) .orElse(str4);ngoại hình không tốt như những Stream.oftôi sẽ sya.
Naman

2
@ OleV.V. Không có gì sai, OP đã biết về điều đó và đang tìm kiếm thứ gì đó cụ thể cho Java-8.
Naman

3
Tôi biết người dùng đang yêu cầu giải pháp cụ thể cho Java-8, nhưng trên một lưu ý chung, tôi sẽ đồng ýStringUtils.firstNonBlank()
Mohamed Anees A

3
Vấn đề là, Java 8 / stream không phải là giải pháp tốt nhất cho việc này. Mã đó thực sự có mùi giống như một bộ tái cấu trúc đang theo thứ tự nhưng nếu không có thêm ngữ cảnh thì thật khó để nói. Đối với người mới bắt đầu - tại sao ba đối tượng có lẽ có quan hệ mật thiết với nhau lại không có trong một bộ sưu tập?
Bill K

2
@MohamedAneesA sẽ đưa ra câu trả lời tốt nhất (dưới dạng nhận xét) nhưng không nêu rõ nguồn của StringUtils trong trường hợp này. Ở mức độ nào đó, nếu bạn PHẢI có những chuỗi này dưới dạng một loạt các chuỗi riêng biệt, thì mã hóa nó dưới dạng phương thức vargs như "firstNonBlank" là lý tưởng, cú pháp bên trong sẽ là một mảng tạo một vòng lặp đơn giản cho mỗi chuỗi với kết quả là tìm một giá trị không null tầm thường và hiển nhiên. Trong trường hợp này, các luồng java 8 là một mối phiền toái hấp dẫn. chúng cám dỗ bạn nội tuyến và phức tạp hóa một cái gì đó phải là một phương thức / vòng lặp đơn giản.
Bill K

Câu trả lời:


172

Bạn có thể làm như vậy:

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

16
Điều này. Suy nghĩ về những gì bạn cần chứ không phải những gì bạn có.
Thorbjørn Ravn Andersen

21
Tốc độ trên đầu là bao nhiêu? Tạo các đối tượng Stream, gọi 4 phương thức, tạo mảng tạm thời ( {str1, str2, str3}) trông giống như chậm hơn nhiều so với cục bộ ifhoặc ?:, mà thời gian chạy Java có thể tối ưu hóa. Có một số tối ưu hóa dành riêng cho Luồng trong javacvà thời gian chạy Java giúp nó nhanh như ?:vậy không? Nếu không, tôi sẽ không đề xuất giải pháp này trong mã quan trọng về hiệu suất.
pts

16
@pts này rất có thể chậm hơn ?:mã và tôi chắc chắn rằng bạn nên tránh nó trong mã quan trọng về hiệu suất. Tuy nhiên, nó dễ đọc hơn nhiều và bạn nên giới thiệu nó trong IMO mã không quan trọng về hiệu suất, mà tôi chắc chắn tạo ra hơn 99% mã.
Aaron

7
@pts Không có tối ưu hóa dành riêng cho Luồng, không trong javaccũng như trong thời gian chạy. Điều này không loại trừ các tối ưu hóa chung, như nội tuyến tất cả các mã, tiếp theo là loại bỏ các hoạt động thừa. Về nguyên tắc, kết quả cuối cùng có thể hiệu quả như các biểu thức điều kiện thuần túy, tuy nhiên, nó không bao giờ đến được đó, vì thời gian chạy sẽ chỉ dành nỗ lực cần thiết cho các đường dẫn mã nóng nhất.
Holger

22
Giải pháp tuyệt vời! Để tăng khả năng đọc một chút, tôi khuyên bạn nên thêm str4vào các tham số của Stream.of(...)và sử dụng orElse(null)cuối cùng.
danielp

73

Làm thế nào về toán tử điều kiện bậc ba?

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

50
trên thực tế, anh ấy đang tìm kiếm "cách tiếp cận tốt nhất để thực hiện việc này trong Java8". Cách tiếp cận này có thể được sử dụng trong Java8, vì vậy tất cả chỉ phụ thuộc vào ý nghĩa của OP là "tốt nhất" và dựa trên cơ sở nào mà anh ta đưa ra quyết định về điều gì tốt hơn.
Stultuske

17
Tôi thường không thích những con chim nhạn lồng nhau, nhưng điều này trông khá sạch sẽ.
JollyJoker

11
Đây là một khó khăn lớn để phân tích cú pháp đối với bất kỳ ai không đọc các toán tử bậc ba lồng nhau mỗi ngày.
Khối

2
@Cubic: Nếu bạn nghĩ rằng nó khó phân tích cú pháp, hãy viết bình luận như // take first non-null of str1..3. Một khi bạn biết nó làm gì, bạn sẽ dễ dàng xem nó như thế nào.
Peter Cordes

4
@Cubic Tuy nhiên, đây là một mẫu lặp lại đơn giản . Khi bạn phân tích cú pháp dòng 2, bạn đã phân tích cú pháp toàn bộ, bất kể bao nhiêu trường hợp được bao gồm. Và khi bạn đã hoàn thành, bạn đã học cách diễn đạt một lựa chọn tương tự gồm mười trường hợp khác nhau một cách ngắn gọn và tương đối đơn giản. Lần tới khi bạn nhìn thấy một ?:cái thang, bạn sẽ biết nó có tác dụng gì. (Btw, các lập trình chức năng biết điều này là (cond ...)mệnh đề:. Nó luôn luôn là một bảo vệ tiếp theo là giá trị thích hợp để sử dụng khi bảo vệ là đúng)
cmaster - Khôi phục monica

35

Bạn cũng có thể sử dụng một vòng lặp:

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

27

Các câu trả lời hiện tại rất hay nhưng bạn thực sự nên đưa nó vào một phương thức hữu ích:

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

Phương pháp đó đã có trong Utillớp của tôi trong nhiều năm, giúp mã sạch hơn nhiều:

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

Bạn thậm chí có thể làm cho nó chung chung:

@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, trong SQL, hàm này được gọi là kết hợp , vì vậy tôi cũng gọi nó như vậy trong mã của mình. Điều đó có hiệu quả với bạn hay không phụ thuộc vào mức độ bạn thích SQL.
Tom Anderson

5
Nếu bạn định đặt nó trong một phương thức tiện ích, bạn cũng có thể làm cho nó hoạt động hiệu quả.
Todd Sewell

1
@ToddSewell, ý bạn là gì?
Gustavo Silva

5
@GustavoSilva Todd có lẽ có nghĩa là, đây là một phương pháp tiện ích của tôi, không có ích gì Arrays.stream()khi tôi có thể làm tương tự với a forvà a != null, hiệu quả hơn. Và Todd sẽ đúng. Tuy nhiên, khi tôi viết mã phương pháp này, tôi đang tìm cách thực hiện việc này bằng cách sử dụng các tính năng của Java 8, giống như OP, vì vậy có điều đó.
walen

4
@GustavoSilva Vâng về cơ bản là vậy: nếu bạn định đặt đây là một hàm sử dụng thì lo ngại về mã sạch không còn quan trọng nữa, và vì vậy bạn cũng có thể sử dụng phiên bản nhanh hơn.
Todd Sewell

13

Tôi sử dụng một chức năng trợ giúp, giống như

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

Sau đó, loại mã này có thể được viết dưới dạng

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

1
Tại sao lại là v0tham số phụ ?
tobias_k

1
@tobias_k Tham số phụ khi sử dụng varargs là cách thành ngữ trong Java để yêu cầu 1 hoặc nhiều đối số thay vì 0 hoặc nhiều hơn. (Xem mục 53 của Java hiệu quả, Ed. 3., sử dụng minlàm ví dụ.) Tôi ít tin rằng điều này là phù hợp ở đây.
Nick

3
@Nick Có, tôi đoán nhiều như vậy, nhưng chức năng sẽ hoạt động tốt (trên thực tế hoạt động giống hệt nhau) nếu không có nó.
tobias_k

1
Một trong những lý do chính để truyền thêm đối số đầu tiên là phải rõ ràng về hành vi khi được truyền vào một mảng. Trong trường hợp này, tôi muốn firstNonNull(arr)trả về arr nếu nó không phải là null. Nếu là firstNonNull(T... vs)nó, nó sẽ trả về mục nhập không phải null đầu tiên trong arr.
Michael Anderson

4

Một giải pháp có thể được áp dụng cho bao nhiêu phần tử bạn muốn có thể là:

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

Bạn có thể tưởng tượng một giải pháp như dưới đây, nhưng giải pháp đầu tiên đảm bảo non nullitycho tất cả các yếu tố

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

6
orElse str4mặc dù nó thực sự là null
Naman

1
@nullpointer Hoặc orElseNull, có str4giá trị tương tự nếu là null.
tobias_k

3

Bạn cũng có thể gộp tất cả các Chuỗi thành một mảng Chuỗi sau đó thực hiện một vòng lặp for để kiểm tra và ngắt khỏi vòng lặp khi nó được gán. Giả sử s1, s2, s3, s4 đều là Chuỗi.

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


s = s4;

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

Đã chỉnh sửa để ném trong điều kiện mặc định thành s4 nếu không có điều kiện nào được gán.


Bạn đã bỏ qua s4(hoặc str4), mà scuối cùng phải được gán cho , ngay cả khi nó là rỗng.
displayName

3

Phương pháp dựa và đơn giản.

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

Và sử dụng nó như:

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

Nó đơn giản để làm với các mảng và trông khá đẹp.


0

Nếu bạn sử dụng Apache Commons Lang 3 thì nó có thể được viết như thế này:

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

Việc sử dụng ObjectUtils.firstNonNull(T...)đã được lấy từ câu trả lời này . Các cách tiếp cận khác nhau cũng được trình bày trong câu hỏi liên quan .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.