Tại sao == so sánh với Integer.valueOf (Chuỗi) cho kết quả khác nhau cho 127 và 128?


182

Tôi không biết tại sao các dòng mã này trả về các giá trị khác nhau:

System.out.println(Integer.valueOf("127")==Integer.valueOf("127"));
System.out.println(Integer.valueOf("128")==Integer.valueOf("128"));
System.out.println(Integer.parseInt("128")==Integer.valueOf("128"));

Đầu ra là:

true
false
true

Tại sao cái thứ nhất trở lại truevà cái thứ hai trở lại false? Có điều gì khác biệt mà tôi không biết giữa 127128không? (Tất nhiên tôi biết rằng 127< 128.)

Ngoài ra, tại sao người thứ ba trở lại true?

Tôi đã đọc câu trả lời của câu hỏi này , nhưng tôi vẫn không hiểu làm thế nào nó có thể trở lại truevà tại sao mã trong dòng thứ hai trở lại false.


6
Số nguyên là một đối tượng; nếu bạn muốn so sánh cho sự bình đẳng, hãy sử dụng .equals(), nếu không tất cả các cược đã tắt.
Karl Damgaard Asmussen

6
@KarlDamgaardAsmussen Trên thực tế ở đây tôi thực sự muốn kiểm tra xem chúng có phải là tham chiếu đến cùng một đối tượng hay không, và lúc đầu tôi không hiểu tại sao 127 128 trả về kết quả khác nhau.
DnR

@DnR nếu Java là một ngôn ngữ có đặc tả được tiêu chuẩn hóa, tôi sẽ nghĩ rằng nó để những vấn đề như vậy xảy ra hoặc thậm chí bắt buộc hành vi không xác định.
Karl Damgaard Asmussen

1
@jszumski: Có nhiều đến này câu hỏi hơn là chỉ phần bộ nhớ đệm, mặc dù. Bên cạnh đó, câu trả lời được liên kết là không đầy đủ nhất - nó không hoàn toàn đi sâu vào chi tiết về những gì được lưu trong bộ nhớ cache và tại sao.
Makoto

1
Để theo dõi thêm về cuộc thảo luận này, vui lòng tham khảo bài viết meta này .
Jeroen Vannevel

Câu trả lời:


191

Có một sự khác biệt nổi bật ở đây.

valueOfđang trả về một Integerđối tượng, có thể có các giá trị của nó được lưu trong khoảng từ -128 đến 127. Đây là lý do tại sao giá trị đầu tiên trả về true- nó được lưu trong bộ nhớ cache - và giá trị thứ hai trả về false- 128 không phải là giá trị được lưu trong bộ nhớ cache, do đó bạn nhận được hai Integertrường hợp riêng biệt .

Điều quan trọng cần lưu ý là bạn đang so sánh các tham chiếu với Integer#valueOfvà nếu bạn đang so sánh một giá trị lớn hơn những gì bộ đệm hỗ trợ, thì nó sẽ không đánh giá được true, ngay cả khi các giá trị được phân tích tương đương (trong trường hợp: Integer.valueOf(128) == Integer.valueOf(128) :). Bạn phải sử dụng equals()thay thế.

parseInt đang trở lại nguyên thủy int . Đây là lý do tại sao giá trị thứ ba trả về true- 128 == 128được đánh giá, và tất nhiên , true.

Bây giờ, một chút công bằng xảy ra để tạo ra kết quả thứ ba đó true:

  • Một chuyển đổi unboxing xảy ra đối với toán tử tương đương bạn đang sử dụng và các kiểu dữ liệu bạn có - cụ thể là intInteger. Bạn đang nhận được một Integertừ valueOfphía bên tay phải, tất nhiên.

  • Sau khi chuyển đổi, bạn đang so sánh hai intgiá trị nguyên thủy . So sánh xảy ra đúng như bạn mong đợi đối với người nguyên thủy, vì vậy bạn kết thúc việc so sánh 128128.


2
@ user3152527: Có một sự khác biệt khá lớn - một đối tượng được coi là một đối tượng, có nghĩa là bạn có thể gọi các phương thức và tương tác với nó trong các cấu trúc dữ liệu trừu tượng, như List. Cái khác là nguyên thủy, chỉ là một giá trị thô.
Makoto

1
@ user3152527 Bạn đã hỏi một câu hỏi xuất sắc (và không phải là một câu hỏi ngớ ngẩn tồi tệ nhất). Nhưng bạn đã sửa nó để sử dụng .equals, phải không?
dùng2910265

3
À, có vẻ như người hỏi không hiểu một sự thật tiềm ẩn trong Java: Khi sử dụng "==" để so sánh hai đối tượng, bạn đang kiểm tra xem chúng có tham chiếu đến cùng một đối tượng không. Khi sử dụng "bằng ()", bạn đang kiểm tra xem chúng có cùng giá trị không. Bạn không thể sử dụng "bằng" để so sánh nguyên thủy.
Jay

3
@Jay không, tôi hiểu điều đó. nhưng điều khiến tôi bối rối lúc đầu là tại sao cái đầu tiên trả về true và cái thứ hai trả về false bằng cùng một phương thức so sánh ==. Dù sao, nó rõ ràng bây giờ.
DnR

1
Nit: không chỉ là số nguyên "có thể" được lưu trong khoảng từ -128 đến 127. Nó phải như vậy, theo JLS 5.1.7 . Nó có thể được lưu trữ bên ngoài phạm vi đó, nhưng không nhất thiết phải (và thường là không).
yshavit

127

Các Integerlớp học có một bộ nhớ cache tĩnh, mà các cửa hàng 256 đặc biệt Integerđối tượng - một cho tất cả các giá trị trong khoảng -128 và 127. Với ý nghĩ đó, hãy xem xét sự khác biệt giữa ba.

new Integer(123);

Điều này (rõ ràng) làm cho một Integerđối tượng hoàn toàn mới .

Integer.parseInt("123");

Điều này trả về một intgiá trị nguyên thủy sau khi phân tích cú pháp String.

Integer.valueOf("123");

Điều này phức tạp hơn những người khác. Nó bắt đầu bằng cách phân tích cú pháp String. Sau đó, nếu giá trị nằm trong khoảng từ -128 đến 127, nó sẽ trả về đối tượng tương ứng từ bộ đệm tĩnh. Nếu giá trị nằm ngoài phạm vi này, thì nó sẽ gọi new Integer()và chuyển vào giá trị, để bạn có được một đối tượng mới.

Bây giờ, hãy xem xét ba biểu thức trong câu hỏi.

Integer.valueOf("127")==Integer.valueOf("127");

Điều này trả về true, bởi vì Integergiá trị của nó là 127 được lấy hai lần từ bộ đệm tĩnh và so với chính nó. Chỉ có một Integerđối tượng liên quan, vì vậy điều này trở lại true.

Integer.valueOf("128")==Integer.valueOf("128");

Điều này trả về false, vì 128 không có trong bộ đệm tĩnh. Vì vậy, một cái mới Integerđược tạo ra cho mỗi bên của sự bình đẳng. Vì có hai Integerđối tượng khác nhau và ==đối với các đối tượng chỉ trả về truenếu cả hai bên là cùng một đối tượng, điều này sẽ xảy ra false.

Integer.parseInt("128")==Integer.valueOf("128");

Đây là so sánh intgiá trị nguyên thủy 128 ở bên trái, với một Integerđối tượng mới được tạo ở bên phải. Nhưng vì không có ý nghĩa gì khi so sánh intvới một Integer, Java sẽ tự động bỏ hộp Integertrước khi thực hiện so sánh; Vì vậy, cuối cùng bạn so sánh một intvới một int. Vì 128 nguyên thủy bằng chính nó, điều này trả về true.


13

Hãy quan tâm đến việc trả lại giá trị từ các phương pháp này. Phương thức valueOf trả về một thể hiện Integer:

public static Integer valueOf(int i)

Các parseInt trở về phương pháp nguyên giá trị (kiểu nguyên thủy):

public static int parseInt(String s) throws NumberFormatException

Giải thích để so sánh:

Để tiết kiệm bộ nhớ, hai phiên bản của các đối tượng trình bao bọc, sẽ luôn là == khi các giá trị nguyên thủy của chúng giống nhau:

  • Boolean
  • Byte
  • Ký tự từ \ u0000 đến \ u007f (7f là 127 ở dạng thập phân)
  • Ngắn và Số nguyên từ -128 đến 127

Khi == được sử dụng để so sánh một nguyên thủy với một trình bao bọc, trình bao bọc sẽ được mở ra và so sánh sẽ là nguyên thủy so với nguyên thủy.

Trong tình huống của bạn (theo các quy tắc trên):

Integer.valueOf("127")==Integer.valueOf("127")

Biểu thức này so sánh các tham chiếu đến cùng một đối tượng vì nó chứa giá trị Integer trong khoảng từ -128 đến 127 nên nó trả về true.

Integer.valueOf("128")==Integer.valueOf("128")

Biểu thức này so sánh các tham chiếu đến các đối tượng khác nhau vì chúng chứa các giá trị Integer không nằm trong <-128, 127> nên nó trả về false.

Integer.parseInt("128")==Integer.valueOf("128")

Biểu thức này so sánh giá trị nguyên thủy (phía bên trái) và tham chiếu đến đối tượng (phía bên phải) vì vậy phía bên phải sẽ không được bao bọc và loại nguyên thủy của anh ta sẽ được so sánh với bên trái để nó quay trở lại true.



Bạn có thể cung cấp một URL cho nguồn báo giá?
Philzen

"... hai trường hợp của các đối tượng trình bao bọc, sẽ luôn là == khi các giá trị nguyên thủy của chúng giống nhau ..." - hoàn toàn sai. Nếu bạn tạo hai đối tượng trình bao bọc có cùng giá trị, chúng sẽ không trả về giá trị true khi so sánh ==vì chúng là các đối tượng khác nhau.
Dawood ibn Kareem

6

Các đối tượng Integer lưu trữ giữa -128 và 127 trên 256 Integer

Bạn không nên so sánh các tham chiếu đối tượng với == hoặc ! = . Bạn nên sử dụng . bằng (..) thay vào đó, hoặc tốt hơn - sử dụng int nguyên thủy thay vì Integer.

parseInt : Phân tích đối số chuỗi dưới dạng số nguyên thập phân đã ký. Tất cả các ký tự trong chuỗi phải là chữ số thập phân, ngoại trừ ký tự đầu tiên có thể là dấu trừ ASCII '-' ('\ u002D') để biểu thị giá trị âm. Giá trị nguyên được kết quả được trả về, chính xác như thể đối số và cơ số 10 được đưa ra làm đối số cho phương thức parseInt (java.lang.String, int).

Trả về một đối tượng Integer giữ giá trị được trích xuất từ ​​Chuỗi được chỉ định khi được phân tích cú pháp với cơ số được cung cấp bởi đối số thứ hai. Đối số đầu tiên được hiểu là đại diện cho một số nguyên đã ký trong cơ số được chỉ định bởi đối số thứ hai, chính xác như thể các đối số được đưa cho phương thức parseInt (java.lang.String, int). Kết quả là một đối tượng Integer đại diện cho giá trị nguyên được chỉ định bởi chuỗi.

tương đương với

new Integer(Integer.parseInt(s, radix))

cơ số - cơ số được sử dụng để giải thích s

vì vậy nếu bạn bằng Integer.valueOf()số nguyên giữa

-128 đến 127 nó trả về đúng trong điều kiện của bạn

cho lesser than-128 và greater than127 nó mang lạifalse


6

Để bổ sung cho các câu trả lời nhất định, cũng lưu ý những điều sau:

public class Test { 
    public static void main(String... args) { 
        Integer a = new Integer(129);
        Integer b = new Integer(129);
        System.out.println(a == b);
    }
}

Mã này cũng sẽ in: false

Như người dùng Jay đã tuyên bố trong một nhận xét cho câu trả lời được chấp nhận, phải cẩn thận khi sử dụng toán tử ==trên các đối tượng, ở đây bạn đang kiểm tra xem cả hai tham chiếu có giống nhau không, bởi vì chúng là các đối tượng khác nhau, mặc dù chúng đại diện cho cùng giá trị. Để so sánh các đối tượng, bạn nên sử dụng equals phương thức thay thế:

Integer a = new Integer(128);
Integer b = new Integer(128);
System.out.println(a.equals(b));

Điều này sẽ in: true

Bạn có thể hỏi, nhưng tại sao dòng đầu tiên được in true? . Kiểm tra mã nguồn cho Integer.valueOfphương thức, bạn có thể thấy như sau:

public static Integer valueOf(String s) throws NumberFormatException {
    return Integer.valueOf(parseInt(s, 10));
}

public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

Nếu param là một số nguyên nằm giữa IntegerCache.low(mặc định là -128) và IntegerCache.high(được tính trong thời gian chạy với giá trị tối thiểu 127) thì một đối tượng được cấp phát trước (được lưu trong bộ nhớ cache) được trả về. Vì vậy, khi bạn sử dụng 127 làm tham số, bạn sẽ nhận được hai tham chiếu đến cùng một đối tượng được lưu trong bộ nhớ cache và trueso sánh các tham chiếu.

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.