2008-09-24 6 views
20

Trong Java, tôi sử dụng một lớp trong đó một số trường có thể là null. Ví dụ:Việc cần làm với các trường rỗng trong so sánh()?

class Foo { 
    String bar; 
    //.... 
} 

Tôi muốn viết một BarComparator cho lớp này,

private static class BarComparator 
      implements Comparator<Foo> { 
     public int compare(final Foo o1, final Foo o2) 
     { 
      // Implementation goes here 
     } 
    } 

Có một cách tiêu chuẩn để đối phó với thực tế là bất kỳ o1, o2, o1.bar, o2.bar có thể null , mà không cần viết nhiều lồng nhau if ... else?

Chúc mừng!

Trả lời

36

Tôi đoán bạn có thể bọc các cuộc gọi đến phương pháp lĩnh vực compareTo với một phương pháp nhỏ tĩnh để sắp xếp null cao hay thấp:

static <T extends Comparable<T>> int cp(T a, T b) { 
    return 
     a==null ? 
     (b==null ? 0 : Integer.MIN_VALUE) : 
     (b==null ? Integer.MAX_VALUE : a.compareTo(b)); 
} 

sử dụng đơn giản (nhiều lĩnh vực được như thường lệ):

public int compare(final Foo o1, final Foo o2) { 
    return cp(o1.field, o2.field); 
} 
+1

Tắt chủ đề tôi biết, nhưng lý do thích MIN/MAX_VALUE hơn -/+ 1 là gì? –

+6

Xin lỗi vì sự chậm trễ trả lời. Đó là để đảm bảo chúng ta có sự bất bình đẳng tam giác. Đối với a> b> c, a.compareTo (b) + b.compareTo (c) <= a.compareTo (c). Không phải ai cũng quan tâm ... –

+0

Vẫn có thể ném 'NullPointerException', nếu o1/o2 là null. Làm thế nào bạn nên điều trị o1/o2 là null? OP được đề cập: o1, o2, o1.bar, o2.bar có thể rỗng. Hoặc là một phần của một hợp đồng so sánh: so sánh nulls nên ném một NPE? – Daniel

1

Dường như với tôi không có phương pháp để làm điều đó, nhưng dù sao mã không quá dài.

0

Tôi nghĩ rằng báo cáo trả lại sớm sẽ là phương án thay thế khác cho nhiều ifs

ví dụ:

if(o1==null) return x; 
if(o2==null) return x; 
if(o1.getBar()==null) return x; 
if(o2.getBar()==null) return x; 

// No null checks needed from this point. 
6

Tùy thuộc vào việc bạn xem mục nhập rỗng có phải là giá trị chuỗi hợp lệ có giá trị so sánh hay không. là null < hoặc> "apple". Điều duy nhất tôi có thể nói chắc chắn là null == null. Nếu bạn có thể xác định nơi null phù hợp với thứ tự sau đó bạn có thể viết mã một cách thích hợp.

Trong trường hợp này tôi có thể chọn để ném một NullPointerExcpetion hoặc IllegalArgumentException và cố gắng xử lý null ở mức cao hơn bằng cách không đặt nó trong so sánh ở nơi đầu tiên.

+0

Tôi thích câu trả lời này. Cảm ơn! – Burkhard

2

Điều quan trọng ở đây là tìm ra cách bạn muốn xử lý null. Một số tùy chọn là: a) giả định null đến trước tất cả các đối tượng khác theo thứ tự sắp xếp b) giả định null đến sau tất cả các đối tượng khác theo thứ tự sắp xếp c) xử lý null tương đương với giá trị mặc định d) xử lý null như điều kiện lỗi. Cái nào bạn chọn sẽ phụ thuộc hoàn toàn vào ứng dụng bạn đang làm việc.

Trong trường hợp cuối cùng, bạn ném một ngoại lệ. Đối với những người khác bạn cần một trường hợp bốn chiều nếu/khác (khoảng ba phút mã hóa một trong những bạn đã làm việc ra những gì bạn muốn kết quả được).

2

Nếu bạn đang sử dụng bộ sưu tập của Google, bạn có thể thấy lớp học Comparators hữu ích. Nếu có các phương thức trợ giúp để đặt hàng các giá trị rỗng là các phần tử lớn nhất hoặc ít nhất trong bộ sưu tập. Bạn có thể sử dụng compound comparators để giúp giảm số lượng mã.

8

Cảm ơn bạn đã trả lời! Phương pháp chung và Google Comparators trông thú vị.

Và tôi phát hiện ra rằng có một NullComparator trong Apache Commons Collections (mà chúng tôi hiện đang sử dụng):

private static class BarComparator 
     implements Comparator<Foo> 
{ 
    public int compare(final Foo o1, final Foo o2) 
    { 
     // o1.bar & o2.bar nulleness is taken care of by the NullComparator. 
     // Easy to extend to more fields. 
     return NULL_COMPARATOR.compare(o1.bar, o2.bar); 
    } 

    private final static NullComparator NULL_COMPARATOR = 
              new NullComparator(false); 
} 

Lưu ý: Tôi tập trung vào các lĩnh vực bar đây để giữ cho nó đến điểm.

+1

Dường như liên kết tới Javadocs đã chết. Nó hiện có tại http://commons.apache.org/proper/commons-collections/javadocs/api-release/index.html. – JBert

1

Bạn không nên sử dụng NullComparator theo cách bạn làm - bạn đang tạo một phiên bản mới của lớp cho mọi hoạt động so sánh và nếu ví dụ: bạn sắp xếp danh sách với 1000 mục nhập, sẽ là 1000 * log2 (1000) đối tượng hoàn toàn thừa. Điều này có thể nhanh chóng gặp sự cố.

Hoặc lớp con nó, hoặc uỷ thác cho nó, hoặc đơn giản là thực hiện kiểm tra null riêng của bạn - nó thực sự không phức tạp:

private static class BarComparator 
     implements Comparator<Foo> { 
    private NullComparator delegate = new NullComparator(false); 

    public int compare(final Foo o1, final Foo o2) 
    { 
     return delegate.compare(o1.bar, o2.bar); 
    } 
} 
+0

Bạn nói đúng, NullComparator phải là một trường tĩnh riêng. Tôi đã viết nó theo cách đó trong ví dụ để tập trung vào sự vô hiệu. –

3

Bạn có thể viết Comparator của bạn cho nó. Cho phép nói rằng bạn có một lớp Person với String name là private field. phương thức getName() và setName() để truy cập tên trường. Dưới đây là Comparator cho Class Person.

Collections.sort(list, new Comparator<Person>() { 
     @Override 
     public int compare(Person a, Person b) { 
      if (a == null) { 
       if (b == null) { 
        return 0; 
       } 
       return -1; 
      } else if (b == null) { 
       return 1; 
      } 
      return a.getName().compareTo(b.getName()); 
     } 
    }); 

Cập nhật:

Tính đến Java 8 bạn có thể sử dụng dưới đây của API cho danh sách.

// Push nulls at the end of List 
Collections.sort(subjects1, Comparator.nullsLast(String::compareTo)); 

// Push nulls at the beginning of List 
Collections.sort(subjects1, Comparator.nullsFirst(String::compareTo)); 
2

Cũng có lớp org.springframework.util.comparator.NullSafeComparator trong khung mùa xuân bạn có thể sử dụng.

Ví dụ (Java 8):

SortedSet<Foo> foos = new TreeSet<>((o1, o2) -> { 
     return new NullSafeComparator<>(String::compareTo, true).compare(o1.getBar(), o2.getBar()); 
    }); 

    foos.add(new Foo(null)); 
    foos.add(new Foo("zzz")); 
    foos.add(new Foo("aaa")); 

    foos.stream().forEach(System.out::println); 

này sẽ in:

Foo{bar='null'} 
Foo{bar='aaa'} 
Foo{bar='zzz'} 
0

Xét khách hàng như một câu trả lời POJO.My sẽ là:

Comparator<Customer> compareCustomer = Comparator.nullsLast((c1,c2) -> c1.getCustomerId().compareTo(c2.getCustomerId())); 

Hoặc

Comparator<Customer> compareByName = Comparator.comparing(Customer::getName,nullsLast(String::compareTo));