Làm thế nào để so sánh đúng hai số nguyên trong Java?


215

Tôi biết rằng nếu bạn so sánh một số nguyên nguyên đóng hộp với hằng số như:

Integer a = 4;
if (a < 5)

a sẽ tự động được bỏ hộp và so sánh sẽ hoạt động.

Tuy nhiên, điều gì xảy ra khi bạn so sánh hai hộp Integersvà muốn so sánh bằng hoặc nhỏ hơn / lớn hơn?

Integer a = 4;
Integer b = 5;

if (a == b)

Mã ở trên sẽ dẫn đến việc kiểm tra xem liệu chúng có phải là cùng một đối tượng không, hoặc nó sẽ tự động hủy hộp trong trường hợp đó?

Thế còn:

Integer a = 4;
Integer b = 5;

if (a < b)

?


16
Vâng, điều gì đã xảy ra khi bạn cố gắng? Bạn đã quan sát cái gì?
Bart Kiers

31
@Bart Kiers: Một thử nghiệm rõ ràng chỉ có thể từ chối, không chứng minh rằng việc bỏ hộp xảy ra. Nếu sử dụng ==thay vì equalsmang lại kết quả chính xác, đó có thể là do các số được đóng hộp đang được sử dụng hoặc sử dụng lại (như là một tối ưu hóa trình biên dịch, có lẽ). Lý do để đặt câu hỏi này là để tìm hiểu những gì đang xảy ra trong nội bộ, chứ không phải những gì dường như đang xảy ra. (Ít nhất, đó là lý do tại sao tôi ở đây.)
Jim Pivarski

Điều gì đã xảy ra với tài khoản của bạn?

Câu trả lời:


301

Không, == giữa Integer, Long, v.v. sẽ kiểm tra đẳng thức tham chiếu - tức là

Integer x = ...;
Integer y = ...;

System.out.println(x == y);

điều này sẽ kiểm tra xem xytham chiếu đến cùng một đối tượng chứ không phải các đối tượng bằng nhau .

Vì thế

Integer x = new Integer(10);
Integer y = new Integer(10);

System.out.println(x == y);

được đảm bảo để in false. Thực hiện các giá trị tự động "nhỏ" có thể dẫn đến kết quả khó khăn:

Integer x = 10;
Integer y = 10;

System.out.println(x == y);

Điều này sẽ in true, do các quy tắc của quyền anh ( phần JLS 5.1.7 ). Nó vẫn tham chiếu bình đẳng đang được sử dụng, nhưng các tài liệu tham khảo thực sự bằng nhau.

Nếu giá trị p được đóng hộp là một số nguyên kiểu chữ int nằm trong khoảng từ -128 đến 127 (§3.10.1), hoặc chữ boolean đúng hoặc sai (§3.10.3) hoặc ký tự bằng chữ giữa '\ u0000' và Bao gồm '\ u007f' (§3.10.4), sau đó cho a và b là kết quả của bất kỳ hai chuyển đổi quyền anh nào của p. Luôn luôn là trường hợp a == b.

Cá nhân tôi sẽ sử dụng:

if (x.intValue() == y.intValue())

hoặc là

if (x.equals(y))

Như bạn nói, đối với mọi so sánh giữa loại trình bao bọc ( Integer, Longv.v.) và loại số ( int, longv.v.), giá trị loại trình bao bọc được bỏ hộp và kiểm tra được áp dụng cho các giá trị nguyên thủy liên quan.

Điều này xảy ra như là một phần của quảng cáo số nhị phân ( phần JLS 5.6.2 ). Nhìn vào tài liệu của từng nhà khai thác để xem liệu nó có được áp dụng hay không. Ví dụ: từ các tài liệu cho ==!=( JLS 15,21.1 ):

Nếu các toán hạng của toán tử đẳng thức đều thuộc loại số hoặc một loại là loại số và loại kia có thể chuyển đổi (§5.1.8) thành loại số, việc quảng cáo số nhị phân được thực hiện trên toán hạng (§5.6.2).

và cho <, <=, >>=( JLS 15.20.1 )

Loại của mỗi toán hạng của toán tử so sánh số phải là loại có thể chuyển đổi (§5.1.8) thành kiểu số nguyên thủy hoặc xảy ra lỗi thời gian biên dịch. Quảng cáo số nhị phân được thực hiện trên các toán hạng (§5.6.2). Nếu kiểu thăng hạng của toán hạng là int hoặc long, thì phép so sánh số nguyên đã ký được thực hiện; nếu loại quảng cáo này là float hoặc double, thì so sánh dấu phẩy động được thực hiện.

Lưu ý làm thế nào không ai trong số này được coi là một phần của tình huống trong đó không loại nào là loại số.


2
Có bất kỳ lý do tại sao một người muốn viết x.compareTo(y) < 0thay vì x < y?
Max Nanasy

1
@MaxNanasy: Không phải là tôi có thể nghĩ ngay đến.
Jon Skeet

2
Kể từ Java 1.6.27+, có một sự quá tải về bằng trong lớp Integer, do đó, nó sẽ hiệu quả như khi gọi .intValue (). Nó so sánh các giá trị như int nguyên thủy.
otterslide

Như @otterslide đã nói, điều này không còn cần thiết trong Java 8 nữa. Việc so sánh Integer với Integer theo giá trị theo mặc định.
Axel Prieto

1
@Axel: Việc thêm quá tải sẽ không thay đổi hành vi của toán tử ==, phải không? Tôi không ở vị trí để kiểm tra ngay bây giờ, nhưng tôi rất ngạc nhiên nếu điều đó đã thay đổi.
Jon Skeet

44

==vẫn sẽ kiểm tra sự bình đẳng đối tượng. Rất dễ bị lừa, tuy nhiên:

Integer a = 10;
Integer b = 10;

System.out.println(a == b); //prints true

Integer c = new Integer(10);
Integer d = new Integer(10);

System.out.println(c == d); //prints false

Các ví dụ của bạn với sự bất bình đẳng sẽ hoạt động vì chúng không được xác định trên Đối tượng. Tuy nhiên, với sự ==so sánh, sự bình đẳng đối tượng vẫn sẽ được kiểm tra. Trong trường hợp này, khi bạn khởi tạo các đối tượng từ một nguyên thủy được đóng hộp, cùng một đối tượng được sử dụng (cho cả a và b). Đây là một tối ưu hóa ổn vì các lớp hộp nguyên thủy là bất biến.


Tôi hình dung đó là sự bình đẳng đối tượng đang được thử nghiệm. Tôi đã có một số kết quả kỳ lạ. Tôi có nên thay thế nó bằng .equals () không? Ngoài ra, bạn có cảm thấy tôi nên để bất bình đẳng như họ hoặc làm theo cách khác không?

Có một số trường hợp cạnh không rõ ràng với autoboxing. Tôi đã cài đặt IDE (Eclipse) của mình để tô màu bất cứ thứ gì không được đóng hộp màu đỏ, điều này đã cứu tôi khỏi các lỗi trong một vài lần. Nếu bạn đang so sánh hai số nguyên, hãy sử dụng .equals, nếu bạn muốn làm cho bất đẳng thức của bạn rõ ràng, hãy viết diễn viên một cách rõ ràng: if ((int) c <(int) d) ...; Bạn cũng có thể làm: c.compareTo (d) <0 // === c <d
Adam Lewis

12
Và nếu bạn thay đổi số chữ thành 200, cả hai bài kiểm tra sẽ in false.
Daniel Earwicker

2
... trên hầu hết các triển khai JVM, đó là. Theo thông số kỹ thuật, kết quả có thể khác nhau giữa các lần thực hiện.
Daniel Earwicker

4
Tôi nghĩ rõ ràng hơn khi gọi đây là "bình đẳng tham chiếu" - theo cách đó rõ ràng là ý của bạn. Tôi thường hiểu "bình đẳng đối tượng" có nghĩa là "kết quả của equalsviệc được gọi".
Jon Skeet

28

Vì Java 1.7, bạn có thể sử dụng Object.equals :

java.util.Objects.equals(oneInteger, anotherInteger);

Trả về true nếu các đối số bằng nhau và sai khác. Do đó, nếu cả hai đối số đều null, true được trả về và nếu chính xác một đối số là null, false sẽ được trả về. Mặt khác, đẳng thức được xác định bằng cách sử dụng phương thức đẳng thức của đối số đầu tiên.


Điều này xử lý null để làm cho nó đơn giản. Cảm ơn!
Darren Parker

10

== kiểm tra sự bằng nhau tham chiếu, tuy nhiên khi viết mã như:

Integer a = 1;
Integer b = 1;

Java đủ thông minh để sử dụng lại cùng một bất biến cho abvì vậy điều này là đúng : a == b. Tò mò, tôi đã viết một ví dụ nhỏ để chỉ ra nơi java ngừng tối ưu hóa theo cách này:

public class BoxingLol {
    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            Integer a = i;
            Integer b = i;
            if (a != b) {
                System.out.println("Done: " + i);
                System.exit(0);
            }
        }
        System.out.println("Done, all values equal");
    }
}

Khi tôi biên dịch và chạy nó (trên máy của tôi), tôi nhận được:

Done: 128

1
tl; dr -1 để rửa tay; stackoverflow.com/questions/15052216/ Quảng stackoverflow.com/questions/20897020/ Thẻ stackoverflow.com/questions/3131136/integers-caching-in-java vv giải thích chi tiết về vấn đề bạn đề cập; đọc tài liệu (hoặc nguồn lib) tốt hơn là tạo các bài kiểm tra giả với nguy cơ kết quả cục bộ cao - không chỉ bạn hoàn toàn quên về giới hạn dưới của bộ đệm (tức là -128 theo mặc định), không chỉ bạn có từng người một (tối đa là 127, không phải 128),

nhưng bạn hoàn toàn không đảm bảo sẽ nhận được kết quả tương tự trên bất kỳ máy nào - vì bạn có thể dễ dàng tăng kích thước bộ đệm, YMMV. Ngoài ra, câu hỏi của OP là làm thế nào để so sánh đúng hai số nguyên - bạn hoàn toàn không trả lời nó .

Tôi tôn trọng ý kiến ​​và nhận thức của bạn ở đây. Tôi nghĩ rằng chúng ta chỉ có những cách tiếp cận cơ bản khác nhau đối với CS.
Cory Kendall

1
đó không phải là về ý kiến cũng như nhận thức - đó là về những sự thật mà bạn chân thành đã bỏ lỡ. Làm một bài kiểm tra giả chứng minh không có gì, không có bất kỳ dữ liệu sao lưu cứng nào (tài liệu, nguồn, v.v.) và không trả lời các câu hỏi của OP không xứng đáng được gọi là Hỏi và Đáp không tốt. Theo "cách tiếp cận khác nhau" - theo định nghĩa, CS là một khoa học ; những gì bạn đã làm khoa học là không ; nó là một tác phẩm câu đố sai lệch (hoặc nó sẽ là một bình luận hấp dẫn , nếu được nêu đúng) - nếu bạn muốn nó là khoa học , hãy sửa những sai sót cơ bản trong câu trả lời của bạn hoặc gỡ rối chúng một cách hợp lý , vì điều đó '

Chắc chắn sau đó, tôi sẽ cố gắng giải quyết các sai sót. Tôi không quên giới hạn dưới, tôi không cảm thấy nó thú vị và chọn không bao gồm nó. Tôi không tin rằng tôi đã bị lỗi do một lỗi, tôi đã nói theo cách mà java (mà tôi đã làm rõ trên máy của mình, trong tình huống của tôi) đã ngừng tối ưu hóa điều này, đó là ở mức 128. Nếu tôi đã tuyên bố giá trị tối đa thì nó đã làm điều này đúng hơn là bạn đúng, câu trả lời sẽ là 127.
Cory Kendall

8

tl; dr ý kiến ​​của tôi là sử dụng một unary +để kích hoạt unboxing trên một trong các toán hạng khi kiểm tra sự bằng nhau về giá trị và chỉ cần sử dụng các toán tử toán học theo cách khác. Lý do sau:

Người ta đã đề cập rằng ==so sánh Integerlà so sánh nhận dạng, thường không phải là điều mà một lập trình viên muốn, và mục đích là để so sánh giá trị; Tuy nhiên, tôi đã thực hiện một khoa học nhỏ về cách thực hiện so sánh đó một cách hiệu quả nhất, cả về độ gọn nhẹ của mã, độ chính xác và tốc độ.

Tôi đã sử dụng một loạt các phương thức thông thường:

public boolean method1() {
    Integer i1 = 7, i2 = 5;
    return i1.equals( i2 );
}

public boolean method2() {
    Integer i1 = 7, i2 = 5;
    return i1.intValue() == i2.intValue();
}

public boolean method3() {
    Integer i1 = 7, i2 = 5;
    return i1.intValue() == i2;
}

public boolean method4() {
    Integer i1 = 7, i2 = 5;
    return i1 == +i2;
}

public boolean method5() { // obviously not what we want..
    Integer i1 = 7, i2 = 5;
    return i1 == i2;
}

và nhận được mã này sau khi biên dịch và dịch ngược:

public boolean method1() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    return var1.equals( var2 );
}

public boolean method2() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method3() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method4() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method5() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2 == var1 ) {
        return true;
    } else {
        return false;
    }
}

Như bạn có thể dễ dàng thấy, các cuộc gọi phương thức 1 Integer.equals() (rõ ràng), các phương thức 2-4 dẫn đến chính xác cùng một mã , hủy kết nối các giá trị bằng phương thức .intValue()và sau đó so sánh chúng trực tiếp và phương thức 5 chỉ kích hoạt so sánh nhận dạng, là cách không chính xác so sánh các giá trị.

Vì (như đã được đề cập bởi ví dụ: JS) equals() phát sinh chi phí hoạt động (nó phải làm instanceofvà không được chọn), nên các phương thức 2-4 sẽ hoạt động với tốc độ chính xác như nhau, tốt hơn đáng kể so với phương pháp 1 khi được sử dụng trong các vòng lặp chặt chẽ, vì HotSpot không có khả năng tối ưu hóa các diễn viên & instanceof.

Nó khá giống với các toán tử so sánh khác (ví dụ </> ) - chúng sẽ kích hoạt unboxing, trong khi sử dụng compareTo()sẽ không - nhưng lần này, hoạt động được HS tối ưu hóa rất cao vì intValue()chỉ là phương thức getter (ứng cử viên chính được tối ưu hóa).

Theo tôi, hiếm khi sử dụng phiên bản 4 là cách ngắn gọn nhất - mọi nhà phát triển C / Java dày dạn đều biết rằng unary plus trong hầu hết các trường hợp tương đương với int/ .intValue()- trong khi đó có thể là một khoảnh khắc WTF nhỏ đối với một số người (chủ yếu là những người đã không Không sử dụng unary plus trong suốt cuộc đời của họ), có thể cho thấy mục đích rõ ràng và chặt chẽ nhất - nó cho thấy rằng chúng tôi muốn một intgiá trị của một trong các toán hạng, cũng buộc giá trị kia phải bỏ hộp. Nó cũng đặc biệt giống với i1 == i2so sánh thông thường được sử dụng cho các intgiá trị nguyên thủy .

Phiếu bầu của tôi dành cho i1 == +i2& i1 > i2phong cách cho Integercác đối tượng, cả vì lý do hiệu suất và tính nhất quán. Nó cũng làm cho mã di động trở thành nguyên thủy mà không thay đổi bất cứ điều gì ngoài khai báo kiểu. Sử dụng các phương pháp được đặt tên có vẻ như giới thiệu tiếng ồn ngữ nghĩa với tôi, tương tự như bigInt.add(10).multiply(-3)phong cách bị chỉ trích nhiều .


Bạn có thể giải thích ý nghĩa của + trong phương pháp 4 không? Tôi đã cố gắng google nó nhưng tôi chỉ nhận được cách sử dụng bình thường của biểu tượng đó (thêm, ghép).
Alex Li

1
@AlexLi nó có nghĩa chính xác những gì tôi đã viết - unary +(unary plus), xem ví dụ stackoverflow.com/questions/2624410/ chủ

8

Gọi điện thoại

if (a == b)

Sẽ hoạt động hầu hết thời gian, nhưng không đảm bảo luôn hoạt động, vì vậy đừng sử dụng nó.

Cách thích hợp nhất để so sánh hai lớp Integer cho đẳng thức, giả sử chúng được đặt tên là 'a' và 'b' là gọi:

if(a != null && a.equals(b)) {
  System.out.println("They are equal");
}

Bạn cũng có thể sử dụng cách này nhanh hơn một chút.

   if(a != null && b != null && (a.intValue() == b.intValue())) {
      System.out.println("They are equal");
    } 

Trên máy của tôi, 99 tỷ thao tác mất 47 giây khi sử dụng phương thức đầu tiên và 46 giây sử dụng phương pháp thứ hai. Bạn sẽ cần phải so sánh hàng tỷ giá trị để thấy bất kỳ sự khác biệt.

Lưu ý rằng 'a' có thể là null vì nó là Object. So sánh theo cách này sẽ không gây ra ngoại lệ con trỏ null.

Để so sánh lớn hơn và ít hơn, sử dụng

if (a != null && b!=null) {
    int compareValue = a.compareTo(b);
    if (compareValue > 0) {
        System.out.println("a is greater than b");
    } else if (compareValue < 0) {
        System.out.println("b is greater than a");
    } else {
            System.out.println("a and b are equal");
    }
} else {
    System.out.println("a or b is null, cannot compare");
}

1
if (a==b)chỉ hoạt động cho các giá trị nhỏ và sẽ không hoạt động hầu hết thời gian.
Tony

Nó hoạt động tới 127 vì đó là bộ đệm Integer mặc định của Java, đảm bảo tất cả các số lên tới 127 có cùng giá trị tham chiếu. Bạn có thể đặt bộ đệm lên cao hơn 127 nếu bạn muốn, nhưng đừng sử dụng == để an toàn.
otterslide

2

Chúng ta nên luôn luôn sử dụng phương thức equals () để so sánh cho hai số nguyên. Thực hành được khuyến nghị.

Nếu chúng ta so sánh hai số nguyên bằng cách sử dụng == sẽ hoạt động với phạm vi giá trị nguyên nhất định (Số nguyên từ -128 đến 127) do tối ưu hóa bên trong của JVM.

Vui lòng xem các ví dụ:

Trường hợp 1:

Số nguyên a = 100; Số nguyên b = 100;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}

Trong trường hợp trên, JVM sử dụng giá trị của a và b từ nhóm được lưu trong bộ nhớ cache và trả về cùng một thể hiện đối tượng (do đó là địa chỉ bộ nhớ) của đối tượng số nguyên và chúng ta nhận được cả hai bằng nhau. Đó là một JVM tối ưu hóa cho các giá trị phạm vi nhất định.

Trường hợp 2: Trong trường hợp này, a và b không bằng nhau vì nó không đi kèm với phạm vi từ -128 đến 127.

Số nguyên a = 220; Số nguyên b = 220;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}

Cách thích hợp:

Integer a = 200;             
Integer b = 200;  
System.out.println("a == b? " + a.equals(b)); // true

Tôi hi vọng cái này giúp được.


1

Trong trường hợp của tôi, tôi đã phải so sánh hai Integers cho sự bình đẳng nơi cả hai có thể null. Tìm kiếm chủ đề tương tự, không tìm thấy bất cứ điều gì thanh lịch cho việc này. Đến với một chức năng tiện ích đơn giản.

public static boolean integersEqual(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return true;
    }
    if (i1 == null && i2 != null) {
        return false;
    }
    if (i1 != null && i2 == null) {
        return false;
    }
    return i1.intValue() == i2.intValue();
}

//considering null is less than not-null
public static int integersCompare(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return 0;
    }
    if (i1 == null && i2 != null) {
        return -1;
    }
    return i1.compareTo(i2);
}

-1

Bởi vì phương thức comparaison phải được thực hiện dựa trên kiểu int (x == y) hoặc lớp Integer (x.equals (y)) với toán tử bên phải

public class Example {

    public static void main(String[] args) {
     int[] arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<arr.length-1; j++)
            if((arr[j-1]!=arr[j]) && (arr[j]!=arr[j+1])) 
                System.out.println("int>"+arr[j]);


    Integer[] I_arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<I_arr.length-1; j++)
            if((!I_arr[j-1].equals(I_arr[j])) && (!I_arr[j].equals(I_arr[j+1]))) 
                System.out.println("Interger>"+I_arr[j]);
    }
}

-2

phương pháp này so sánh hai số nguyên với kiểm tra null, xem các bài kiểm tra

public static boolean compare(Integer int1, Integer int2) {
    if(int1!=null) {
        return int1.equals(int2);
    } else {
        return int2==null;
    }
    //inline version:
    //return (int1!=null) ? int1.equals(int2) : int2==null;
}

//results:
System.out.println(compare(1,1));           //true
System.out.println(compare(0,1));           //false
System.out.println(compare(1,0));           //false
System.out.println(compare(null,0));        //false
System.out.println(compare(0,null));        //false
System.out.println(compare(null,null));     //true

4
Đối với điều này, tôi nghĩ sẽ tốt hơn nếu sử dụng Objects.equals(x,y)phương pháp thay vì tự lăn.
ryvantage
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.