Tại sao 1 == 1 lại đúng trong khi 1000 == 1000 thì sai với Integer wrappers trong Java?

Tram Ho

Hi foks!

Tuần này mình lại sml với bọn Java (unit test), thiết nghĩ chỉ viết mỗi unit test sẽ sớm bị “out trình” Java nên mình lân la trên Google, StackOverflow tìm vài món Java hack não để luyện tập, và mình vô tình lướt qua 1 câu hỏi thú vị (như tiêu đề). Bài viết này mình sẽ chia sẻ với bạn đọc những thứ hay ho của Java mà mình học được được sau khi giải ngố câu hỏi trên.

1. Đặt vấn đề

Để làm rõ vấn đề trong câu hỏi chính của bài viết, cùng mình xem qua đoạn code Java bên dưới

Và kết quả sau khi chạy màn main

Để có thể giải thích kết quả trên, trước tiên cùng mình ôn lại một số kiến thức Java căn bản

2. So sánh Object trong Java

Để so sánh 2 objects trong Java, chắc hẳn chúng ta đều biết không nên sử dụng toán tử ==, toán tử này so sánh tham chiếu của 2 objects thay vì giá trị của chúng.

Tham chiếu là địa chỉ bộ nhớ mà tại đó các objects sẽ được lưu trữ

Ví dụ:

Đoạn code đầu bài sử dụng toán tử ==. Dangerous!!!

3. Wrapper classes & Auto boxing

Trong Java, chúng ta có 2 cách để thể hiện giá trị integer: sử dụng primitive types hoặc wrapper classes.

primitive types sẽ đơn giản là lưu giá trị một cách trực tiếp, như int, float, double,…

wrapper classes thì lại là Object trong Java, nó sẽ bọc giá trị của chúng ta trong 1 object, như Integer là object và bên trong sẽ là một giá trị int.

Một trong những lợi ích của wrapper classes mà mình hay trải nghiệm đó là “đẻ” ra NullPointerException. Đùa 1 tí, primitive types và wrapper classes sẽ giúp chúng ta giải quyết vấn đề “tham chiếu, tham trị” khi truyền đối số vào method (và nhiều lợi ích khác, các bạn có thể Google để tìm hiểu thêm).

Auto boxing là một cơ chế của Java compilier, compilier sẽ tự động convert primitive types sang wrapper class tương ứng. Ví dụ integer thành Integer, double thành Double, cách dùng auto boxing đơn giản là gán trực tiếp primitive types value cho wrapper classes

Đến đây, chúng ta cùng quay lại đoạn code đầu bài, phân tích một chút

Dựa vào kiến thức đầu bài đến giờ, chúng ta dễ dàng xác định được a == b, c == d đều cho kết quả FALSE, nhưng kết quả của chương trình hơi “cấn cấn” nhỉ. Đó là vì mình thiếu 1 kiến thức thú vị bên dưới.

4. Integer caching

“Object creation is expensive” bởi vậy nên Integer class có thứ gọi là integer caching, một kĩ thuật để optimize performance, giúp chúng ta tái sử dụng lại các Integer objects thay vì phải tạo mới object mỗi lần sử dụng. Trước tiên, cùng xem implement của method Integer.valueOf (method behind the scenes của autoboxing)

Sự xuất hiện của class IntegerCache và javaDoc đã thể hiện rõ ràng, Integer dùng class IntegerCache để cache lại các objects, cụ thể có range value từ -128 đến 127. Vì vậy, ab – 2 objects dùng autoboxing sẽ trỏ đến cùng 1 object (chứa value int 1) vì giá trị int của chúng nằm trong range value caching, do đó khi so sánh == cho ra giá trị TRUE.

Đối với e, do sử dụng new keyword, nó sẽ được tạo riêng 1 object mới và vì thế nên so sánh == với a sẽ cho ra FALSE, tương tự khi so sánh b == e

5. Kết

Vậy là chúng ta vừa ôn lại một vài kiến thức thú vị trong Java, đặc biệt là kiến thức về so sánh 2 objects, chúng ta (luôn) nên dùng equals thay vì ==, trừ khi giá trị của chúng nằm trong vùng cache đối với Integer. Hiện tại Double, Float chưa có cơ chế cache này.

6. Tham khảo

Autoboxing: https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html

Integer caching: https://howtodoinjava.com/java-examples/internal-cache-wrapper-classes/

Hẹn gặp lại các bạn trong những bài sau. Cheers!

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo