Integer Caching
Vấn đề
Phần tiêu đề “Vấn đề”== 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!
Vì sao xảy ra?
Phần tiêu đề “Vì sao xảy ra?”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ượngInteger 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ầnInteger 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.
Giải pháp
Phần tiêu đề “Giải pháp”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 objectTóm tắt
Phần tiêu đề “Tóm tắt”Integer caching: JVM tái sử dụng đối tượng
Integercho 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ảiInteger)