2015-05-26 26 views
6

Tôi gặp vấn đề này một thời gian, đã tìm kiếm rất nhiều câu hỏi StackOverflow nhưng không thể giải quyết được vấn đề của tôi.Tại sao đôi khi tôi so sánh methd ném IllegalArgumentException?

Tôi cũng hỏi một câu hỏi tương tự trước và có những gợi ý để sử dụng,

System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); 

Nó đã không giải quyết vấn đề của tôi. Tôi không bao giờ có ngoại lệ này trên bất kỳ thiết bị thử nghiệm nào của mình, nhưng một số người dùng của tôi đã báo cáo nó thường xuyên. Tôi thực sự không biết làm thế nào để giải quyết nó.

Các ngoại lệ

Đây là ngoại lệ mà tôi nhận được,

java.lang.IllegalArgumentException: Comparison method violates its general contract! 
at java.util.TimSort.mergeLo(TimSort.java:743) 
at java.util.TimSort.mergeAt(TimSort.java:479) 
at java.util.TimSort.mergeCollapse(TimSort.java:404) 
at java.util.TimSort.sort(TimSort.java:210) 
at java.util.TimSort.sort(TimSort.java:169) 
at java.util.Arrays.sort(Arrays.java:2023) 
at java.util.Collections.sort(Collections.java:1883) 

hoặc đôi khi điều này,

java.lang.IllegalArgumentException: Comparison method violates its general contract! 
at java.util.TimSort.mergeHi(TimSort.java:864) 
at java.util.TimSort.mergeAt(TimSort.java:481) 
at java.util.TimSort.mergeCollapse(TimSort.java:406) 
at java.util.TimSort.sort(TimSort.java:210) 
at java.util.TimSort.sort(TimSort.java:169) 
at java.util.Arrays.sort(Arrays.java:2010) 
at java.util.Collections.sort(Collections.java:1883) 

gì I Have Done

enum FileItemComparator implements Comparator<FileItem> { 

    //Using ENUM 
    NAME_SORT { 
     public int compare(FileItem o1, FileItem o2) { 

      int result = 0; 
      if (o1 != null && o2 != null) { 

       String n1 = o1.getFileName(); 
       String n2 = o2.getFileName(); 

       if (n1 != null && n2 != null) 
        result = n1.compareTo(n2); 
      } 

      return result; 
     } 
    }, 
    DATE_SORT { 
     public int compare(FileItem o1, FileItem o2) { 

      int result = 0; 
      if (o1 != null && o2 != null) { 

       String d1 = o1.getFileDate(); 
       String d2 = o2.getFileDate(); 

       if (d1 != null && d2 != null) { 

        Long l1 = Long.valueOf(d1); 
        Long l2 = Long.valueOf(d2); 

        if (l1 != null && l2 != null) { 
         result = l1.compareTo(l2); 
        } 
       } 

      } 

      return result; 
     } 
    }, 
    SIZE_SORT { 
     public int compare(FileItem o1, FileItem o2) { 

      int result = 0; 
      if (o1 != null && o2 != null) { 

       File f1 = o1.getItem(); 
       File f2 = o2.getItem(); 

       if (f1 != null && f2 != null) { 

        result = Long.valueOf(f1.length()).compareTo(Long.valueOf(f2.length())); 
       } 
      } 

      return result; 
     } 
    }; 

    public static Comparator<FileItem> descending(final Comparator<FileItem> other) { 

     return new Comparator<FileItem>() { 
      public int compare(FileItem o1, FileItem o2) { 
       return -1 * other.compare(o1, o2); 
      } 
     }; 
    } 

    public static Comparator<FileItem> getComparator(final FileItemComparator... multipleOptions) { 
     return new Comparator<FileItem>() { 
      public int compare(FileItem o1, FileItem o2) { 
       for (FileItemComparator option : multipleOptions) { 
        int result = option.compare(o1, o2); 
        if (result != 0) { 
         return result; 
        } 
       } 
       return 0; 
      } 
     }; 
    } 
} 

Đây là cách tôi đang phân loại,

Collections.sort(dirs, FileItemComparator.getComparator(FileItemComparator.NAME_SORT)); 

Vấn đề

Tôi chắc chắn có cái gì đó sai trong các so sánh phương pháp với sự phụ thuộc bắc cầu. Tôi đã thử rất nhiều và dường như không thể sửa nó. Trên thực tế, tôi chưa bao giờ gặp sự cố này trong bất kỳ thiết bị thử nghiệm nào của mình, nhưng người dùng của tôi đang báo cáo nó liên tục.

Tôi hy vọng mọi người ở đây sẽ có thể nắm bắt được vấn đề và giúp tôi giải quyết nó một lần và cho tất cả.

Cập nhật Mã (Nhờ @Eran)

Tôi nghĩ rằng nó sẽ là tốt nhất để giúp đỡ người khác bằng cách đăng các mã được cập nhật đầy đủ. Nó sẽ giúp rất nhiều người phải đối mặt với cùng một vấn đề.

enum FileItemComparator implements Comparator<FileItem> { 

    //Using ENUM 
    NAME_SORT { 
     public int compare(FileItem o1, FileItem o2) { 

      if (o1 == null) { 
       if (o2 == null) { 
        return 0; 
       } else { 
        return 1; // this will put null in the end 
       } 
      } else if (o2 == null) { 
       return -1; 
      } 

      String n1 = o1.getFileName(); 
      String n2 = o2.getFileName(); 

      if (n1 == null) { 
       if (n2 == null) { 
        return 0; 
       } else { 
        return 1; // this will put null names after non null names 
       } 
      } else if (n2 == null) { 
       return -1; 
      } 
      return n1.compareTo(n2); 
     } 
    }, 
    DATE_SORT { 
     public int compare(FileItem o1, FileItem o2) { 

      if (o1 == null) { 
       if (o2 == null) { 
        return 0; 
       } else { 
        return 1; // this will put null in the end 
       } 
      } else if (o2 == null) { 
       return -1; 
      } 

      String d1 = o1.getFileDate(); 
      String d2 = o2.getFileDate(); 

      if (d1 == null) { 
       if (d2 == null) { 
        return 0; 
       } else { 
        return 1; // this will put null names after non null names 
       } 
      } else if (d2 == null) { 
       return -1; 
      } 

      Long l1 = Long.valueOf(d1); 
      Long l2 = Long.valueOf(d2); 

      if (l1 == null) { 
       if (l2 == null) { 
        return 0; 
       } else { 
        return 1; // this will put null names after non null names 
       } 
      } else if (l2 == null) { 
       return -1; 
      } 

      return l1.compareTo(l2); 
     } 
    }, 
    SIZE_SORT { 
     public int compare(FileItem o1, FileItem o2) { 

      if (o1 == null) { 
       if (o2 == null) { 
        return 0; 
       } else { 
        return 1; // this will put null in the end 
       } 
      } else if (o2 == null) { 
       return -1; 
      } 

      File f1 = o1.getItem(); 
      File f2 = o2.getItem(); 

      if (f1 == null) { 
       if (f2 == null) { 
        return 0; 
       } else { 
        return 1; // this will put null in the end 
       } 
      } else if (f2 == null) { 
       return -1; 
      } 

      Long l1 = Long.valueOf(f1.length()); 
      Long l2 = Long.valueOf(f2.length()); 

      if (l1 == null) { 
       if (l2 == null) { 
        return 0; 
       } else { 
        return 1; // this will put null names after non null names 
       } 
      } else if (l2 == null) { 
       return -1; 
      } 

      return l1.compareTo(l2); 
     } 
    }; 

    public static Comparator<FileItem> descending(final Comparator<FileItem> other) { 

     return new Comparator<FileItem>() { 
      public int compare(FileItem o1, FileItem o2) { 
       return -1 * other.compare(o1, o2); 
      } 
     }; 
    } 

    public static Comparator<FileItem> getComparator(final FileItemComparator... multipleOptions) { 
     return new Comparator<FileItem>() { 
      public int compare(FileItem o1, FileItem o2) { 
       for (FileItemComparator option : multipleOptions) { 
        int result = option.compare(o1, o2); 
        if (result != 0) { 
         return result; 
        } 
       } 
       return 0; 
      } 
     }; 
    } 
} 
+0

Nó có thể liên quan với một trong các đối số là 'null' và không phải là đối số khác, nhưng nó sẽ trả về' 0'? – Mena

+0

@Mena Có, điều này là có thể. Tốt tìm. Bạn có thể vui lòng giúp tôi với một mã số làm việc? –

+0

có vẻ như [Eran] ​​(http://stackoverflow.com/users/1221571/eran) vừa làm :) – Mena

Trả lời

9

Hãy xem xét đầu tiên phương pháp so sánh của bạn:

public int compare(FileItem o1, FileItem o2) { 

     int result = 0; 
     if (o1 != null && o2 != null) { 

      String n1 = o1.getFileName(); 
      String n2 = o2.getFileName(); 

      if (n1 != null && n2 != null) 
       result = n1.compareTo(n2); 
     } 

     return result; 
    } 

Giả sử bạn đang so sánh hai FileItems (chúng ta hãy gọi chúng o1 và o2), một với một tên tập tin và người kia mà không (tức là tập rỗng Tên). Phương thức của bạn sẽ trả về 0.

Bây giờ nếu bạn so sánh o2 với một FileItem (o3) khác mà tên tệp không rỗng, bạn trả về 0 lần nữa.

Nhưng nếu bạn so sánh o1 với o3, vì cả hai đều có tên tệp không rỗng, so sánh trả về -1 hoặc 1 (giả sử tên tệp khác nhau).

Do đó, so sánh của bạn không nhất quán vì nó không phải là chuyển tiếp.

Nếu một phần tử thiếu thuộc tính bắt buộc để so sánh và phần tử khác không có, bạn không nên trả về 0.Bạn nên quyết định trả về 1 hay -1 (tùy thuộc vào việc, ví dụ, FileItems có tên null phải được đặt trước hoặc sau FileItems với tên không null).

Ví dụ:

public int compare(FileItem o1, FileItem o2) 
{ 
    if (o1 == null) { 
     if (o2 == null) { 
      return 0; 
     } else { 
      return 1; // this will put null in the end 
     } 
    } else if (o2 == null) { 
     return -1; 
    } 
    String n1 = o1.getFileName(); 
    String n2 = o2.getFileName(); 
    if (n1 == null) { 
     if (n2 == null) { 
      return 0; 
     } else { 
      return 1; // this will put null names after non null names 
     } 
    } else if (n2 == null) { 
     return -1; 
    } 
    return n1.compareTo(n2); 
} 
+0

Đó là một lời giải thích tuyệt vời. Bạn có thể vui lòng đăng một mã hoạt động cho phương pháp so sánh không? Tôi không muốn lặp lại bất kỳ sai lầm nào nữa. –

+0

@Aritra xem chỉnh sửa. Đã không kiểm tra nó, nhưng có vẻ đúng. – Eran

+0

Cảm ơn rất nhiều vì điều đó. Hãy để tôi kiểm tra nó, xin vui lòng. Btw, trong bản chỉnh sửa mới nhất bạn vừa đảo ngược logic, để đặt null vào cuối bạn đang quay trở lại 1. Đó có phải là hành vi đúng không? Một lời giải thích như trước đây sẽ thực sự hữu ích. –

3

Đây là một lỗi phổ biến với bộ so sánh - bạn không xử lý null cách nhất quán. Các mô hình thông thường sẽ trông như thế này:

public int compare(FileItem o1, FileItem o2) { 
    // null == null 
    if (o1 == null && o2 == null) { 
     return 0; 
    } 
    // null < not null 
    if (o1 == null || o2 == null) { 
     return -1; 
    } 
    // Neither can be null now so this is safe. 
    String n1 = o1.getFileName(); 
    String n2 = o2.getFileName(); 
    // Same logic again. 
    if (n1 == null && n2 == null) { 
     return 0; 
    } 
    if (n1 == null || n2 == null) { 
     return -1; 
    } 
    return n1.compareTo(n2); 
} 

Added

Lưu ý rằng việc thực hiện này cũng một lỗi phổ biến như tôi cho phép compare(null,not_null) để bằng compare(not_null,null) mà cũng vi phạm hợp đồng - vui lòng sử dụng @Eran's solution hoặc một cái gì đó như thế này.

public int compare(FileItem o1, FileItem o2) { 
    // null == null 
    if (o1 == null && o2 == null) { 
     return 0; 
    } 
    // null != not null 
    if (o1 == null || o2 == null) { 
     // Swap these around if you want 'null' at the other end. 
     return o1 == null ? -1: 1; 
    } 
    // Neither can be null now so this is safe. 
    String n1 = o1.getFileName(); 
    String n2 = o2.getFileName(); 
    // Same logic again. 
    if (n1 == null && n2 == null) { 
     return 0; 
    } 
    if (n1 == null || n2 == null) { 
     // Swap these around if you want 'null' at the other end. 
     return n1 == null ? -1: 1; 
    } 
    return n1.compareTo(n2); 
} 
+2

Tôi không nghĩ rằng sự so sánh này là nhất quán. so sánh (null, không null) và so sánh (không null, null) sẽ trả về -1 với mã này. – Eran

+0

@Eran - Đó là một điểm tốt - Tôi không có một ngày tốt lành hôm nay. :) – OldCurmudgeon

Các vấn đề liên quan