Bỏ qua để đến nội dung

Integer Caching

== trên hai đối tượng Integer (wrapper class của int) so sánh tham chiếu. Vậy mà đoạn code sau lại cho hai kết quả khác nhau một cách khó hiểu:

Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true !?
Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false !?

Cùng một kiểu so sánh, cùng cách khai báo, chỉ khác giá trị số - mà kết quả lại khác nhau!

Java có một cơ chế gọi là Integer caching: với autoboxing (int -> Integer tự động), JVM tái sử dụng các đối tượng Integer đã tạo sẵn cho các giá trị trong khoảng -128 đến 127 (theo đặc tả Integer.valueOf()), để tiết kiệm bộ nhớ vì đây là các giá trị số nguyên nhỏ rất thường dùng.

Integer a = 100; // 100 nằm trong [-128, 127] -> lấy từ cache, a và b CÙNG một đối tượng
Integer b = 100;
System.out.println(a == b); // true - vì cùng tham chiếu tới đối tượng cache
Integer c = 200; // 200 NGOÀI khoảng [-128, 127] -> tạo đối tượng MỚI mỗi lần
Integer d = 200;
System.out.println(c == d); // false - hai đối tượng khác nhau trên heap

Đây chính là lý do hai đoạn code “giống hệt cấu trúc” lại cho kết quả khác nhau chỉ vì giá trị số khác nhau - một trong những cạm bẫy kinh điển và gây bối rối nhất của Java.

Không bao giờ dùng == để so sánh giá trị của hai đối tượng Integer (hay bất kỳ wrapper class nào: Long, Short, Character,…) - luôn dùng .equals():

Integer a = 200;
Integer b = 200;
System.out.println(a.equals(b)); // true - luôn đúng, không phụ thuộc vào giá trị
// Hoặc so sánh dưới dạng kiểu nguyên thủy (unbox trước khi so sánh):
System.out.println(a.intValue() == b.intValue()); // true - so sánh giá trị int thực sự

⚠️ Lưu ý: nếu khai báo bằng kiểu nguyên thủy int (không phải Integer), == hoạt động đúng như mong đợi vì lúc đó so sánh giá trị, không phải tham chiếu:

int x = 200;
int y = 200;
System.out.println(x == y); // true - đúng, vì x, y là kiểu nguyên thủy, không phải object

Integer caching: JVM tái sử dụng đối tượng Integer cho giá trị trong khoảng [-128, 127], khiến == “tình cờ đúng” trong khoảng này nhưng sai ngoài khoảng đó.

  • Luôn dùng .equals() để so sánh giá trị của các wrapper class (Integer, Long,…)
  • == chỉ an toàn để so sánh giá trị khi cả hai biến là kiểu nguyên thủy (int, không phải Integer)