Java Integer CompareTo () - tại sao lại sử dụng phép so sánh và phép trừ?


80

Tôi thấy rằng java.lang.Integerviệc triển khai compareTophương thức trông như sau:

public int compareTo(Integer anotherInteger) {
    int thisVal = this.value;
    int anotherVal = anotherInteger.value;
    return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
}

Câu hỏi đặt ra là tại sao lại sử dụng phép so sánh thay vì phép trừ:

return thisVal - anotherVal;

27
Khi chúng ta quá lo lắng về việc tối ưu hóa vi mô, chúng ta thường kết thúc với mã lỗi.
Kevin Bourrillion

Kể từ JDK 7, người ta có thể sử dụng Integer.compare(thisVal, anotherVal)thay vì viết ra biểu thức bậc ba.
Stuart Marks

Câu trả lời:


96

Điều này là do tràn số nguyên. Khi nào thisVallà rất lớn và anotherVallà số âm thì trừ đi giá trị thứ hai với giá trị trước đó sẽ tạo ra kết quả lớn hơn thisValcó thể tràn sang phạm vi âm.


Vâng, cách họ đã làm ở đây có lẽ hiệu quả hơn so với việc kiểm tra tràn và cộng sự
rogerdpack

Sử dụng Guava ComparisonChain. Nó rất tiện dụng! google.github.io/guava/releases/22.0/api/docs/com/google/common/...
Andrea Bergonzo

thisValkhông cần phải lớn. thisValthậm chí có thể bằng 0 và anotherValcó thể là không Integer.MIN_VALUEvà bạn đã bị tràn. Và hãy nhớ rằng, tất nhiên, nó cũng có thể là một chiều ngược lại, thisValuerất nhỏ và anotherValkhá lớn, có khoảng cách vượt quá intphạm vi giá trị.
Holger

65

Phép trừ "mẹo" để so sánh hai giá trị số bị hỏng !!!

        int a = -2000000000;
        int b =  2000000000;
        System.out.println(a - b);
        // prints "294967296"

Đây, a < bnhưng a - blà tích cực.

KHÔNG sử dụng thành ngữ này. Nó không hoạt động.

Hơn nữa, ngay cả khi nó hoạt động , nó sẽ KHÔNG cung cấp bất kỳ cải tiến đáng kể nào về hiệu suất và trên thực tế, có thể tốn kém khả năng đọc.

Xem thêm

  • Câu đố Java Puzzlers Câu đố 65: Một Saga kỳ lạ của sự sắp xếp đáng ngờ

    Câu đố này có một số bài học. Cụ thể nhất là: Không sử dụng bộ so sánh dựa trên phép trừ trừ khi bạn chắc chắn rằng sự khác biệt giữa các giá trị sẽ không bao giờ lớn hơn Integer.MAX_VALUE . Nói chung, hãy cẩn thận với inttràn. Một bài học khác là bạn nên tránh mã "thông minh". Cố gắng viết mã rõ ràng, chính xác và không tối ưu hóa nó trừ khi chứng minh được là cần thiết.


2
Nó không thực sự bị hỏng chút nào. Nếu bạn biết bất cứ điều gì về những con số bạn đang so sánh, có thể bạn sẽ biết rằng chúng an toàn để so sánh. Ngay cả khi không biết, chỉ ((long)a - b)nên làm việc. Mặc dù bạn đúng; nó rất hiếm khi hữu ích.
amara

4
@naiad vừa làm ((long)a - b)không giúp ích được gì, vì bạn phải truyền kết quả trở lại int, vì đó là những gì bộ so sánh phải trả về, kết thúc một lần nữa với một tràn. Bạn sẽ phải làm điều gì đó như Long.signumtrên kết quả, điều này rất dễ bị quên, như nhận xét của bạn hiển thị. Và nó thậm chí có thể không hiệu quả hơn Integer.compare, điều mà JVM có thể xử lý về bản chất…
Holger

9

Nói một cách đơn giản, intkiểu không đủ lớn để lưu trữ sự khác biệt giữa hai intgiá trị tùy ý . Ví dụ, chênh lệch giữa 1,5 tỷ và -1,5 tỷ là 3,0 tỷ, nhưng intkhông được giữ giá trị lớn hơn 2,1 tỷ.



1

Ngoài vấn đề tràn, bạn cần lưu ý rằng phiên bản có phân số không cho kết quả tương tự .

  • Phiên bản CompareTo đầu tiên trả về một trong ba giá trị có thể có: -1, 0 hoặc 1.
  • Nếu bạn thay thế dòng cuối cùng bằng phân số, kết quả có thể là bất kỳ giá trị nguyên nào.

Nếu bạn biết sẽ không có tràn, bạn có thể sử dụng một cái gì đó như sau:

public int compareTo(Integer anotherInteger) {
    return sign(this.value - anotherInteger.valuel);
}

12
Bạn đúng là kết quả không giống nhau. Nhưng chúng không bắt buộc phải như vậy! compareTochỉ được yêu cầu trả về giá trị âm, không hoặc giá trị dương, tùy thuộc vào thứ tự sắp xếp của thisvà đối tượng khác. Xem java.sun.com/j2se/1.5.0/docs/api/java/lang/…
Christian Semrau
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.