NỘI DUNG BÀI VIẾT
Trong Java, String là một nội dung quan trọng mà các lập trình viên Java cần phải nắm được. Và bên trong String lại có rất nhiều các concept khác nhau và một trong các concept liên quan đến String đó là String Pool. Đây sẽ là concept mà chúng ta sẽ cùng nhau tìm hiểu trong bài này!
1. String Pool là gì?
Cũng như bao concept khác, để chúng ta có thể tìm hiểu được một nội dung hay một chủ đề nào đó bao giờ cũng cần phải xuất phát từ việc biết concept đó là gì. Và để hiểu được String Pool chúng ta cần trả lời được các câu hỏi sau. String Pool là gì? String Pool chúng ta có thể hiểu một cách đơn giản đó là một vùng nhớ để lưu trữ. Vậy String pool sẽ được lưu trữ ở đâu và nó lưu trữ những gì? String Pool thì được đặt bên trong bộ nhớ Heap và được sử dụng để lưu trữ các String literal. Nhưng bạn chưa biết về khái niệm String literal thì có thể quan sát hình dưới đây:
2. Lợi ích của String Pool
Khi mà chúng ta chưa có String Pool thì việc khởi tạo ra các String sẽ gây tốn vùng nhớ trong bộ nhớ Heap và khi chúng ta ra quá nhiều các String thì nó sẽ gây lãng phí bộ nhớ và khiến giảm performance của chương trình.
Vậy khi có String Pool thì có gì khác? Khi có String Pool thì sẽ giúp tăng performance của chương trình cũng như giảm thiểu được các vùng nhớ lưu trữ các String literal. Vì khi chúng ta tạo một String literal thì chương trình trước tiên sẽ kiểm tra xem String đó đã có trong String Pool hay chưa? Nếu đã có trong String Pool thì nó sẽ tái sử dụng lại vùng nhớ đó thay vì tạo ra một vùng nhớ mới. Và nếu chưa có trong String Pool thì nó sẽ tạo ra một vùng nhớ cho String đó.
3. Cơ chế hoạt động của String Pool
3.1. Khai báo chuỗi trong Java
Trong Java cung cấp cho chúng ta hai cách khai báo chuỗi như sau:
Khai báo chuỗi sử dụng String literal
Ví dụ:
String x = "Hello World";
String name = "John";
Khai báo chuỗi sử dụng bằng từ khóa new
Ví dụ:
String x = new String("Hello World");
String name = new String ("John");
Vậy giữa 2 cách khai báo này có gì khác nhau hay không? Câu trả lời là có.
- Với cách khai báo đầu tiên khi chúng ta sử dụng String literal các String được tạo ra sẽ được lưu trữ vào bên trong String Pool do đó nếu String được khai báo đã có trong String Pool thì nó sẽ không tạo thêm ô nhớ nữa mà sẽ tái sử dụng lại String đó giúp tiết kiệm được bộ nhớ.
- Còn đối với cách thứ 2 sử dụng từ khóa new thì sao? Khi chúng ta sử dụng từ khóa
new
chương trình sẽ hiểu là chúng ta đang muốn tạo ra một reference variable do đó nó sẽ tạo một biến tham chiếu tương ứng ở trong bộ nhớ Heap. Nên mỗi lần chúng ta sử dụng từ khóanew
để khai báo một chuỗi nó sẽ tạo ra một vùng nhớ mới để lưu trữ.
3.2. Cơ chế hoạt động của String Pool
Nói đến đây có lẽ các bạn cũng đã hình dung được phần nào về String Pool rồi nhưng để rõ hơn các bạn cũng có thể quan sát ví dụ sau:
Giả sử mình sẽ khai báo các chuỗi như sau:
String x = "Hello World";
String y = "Hello World";
String z = new String("Hello World");
Ở ví dụ trên cả 3 biến x, y, z
của mình đều được sử dụng để khởi tạo một chuỗi "Hello World"
vậy chuỗi này sẽ được tổ chức lưu trữ như thế nào cùng quan sát hình dưới đây nhé:
Ở hình vẽ trên chúng ta đã thấy rằng 2 biến chuỗi x
và y
đang cùng sử dụng chung 1 ô nhớ trong String Pool. Trong đó chuỗi "Hello World"
sẽ được tạo khi khai báo biến x
. Và sau đó khi khai báo biến y
cũng bằng "Hello World"
thì nó sẽ lấy chuỗi "Hello World"
đã được khai báo trong String Pool và gán cho y
. Vậy nên khi chúng ta thực hiện so sánh x == y
nó sẽ so sánh ô nhớ của 2 biến này xem chúng có trùng nhau hay không và như hình vẽ trên chúng đang sử dụng chung ô nhớ nên kết quả thu được sẽ là true
.
4. Sự khác nhau giữa == và equals() khi so sánh 2 chuỗi
Nếu nhìn sơ qua chúng ta đều thấy == à equal() đều có nghĩa là = hay nó cách khác nó đều được sử dụng để so sánh xem 2 chuỗi đó có bằng nhau hay không? Nhưng giữa chúng có gì khác nhau?
4.1. Toán tử ==
Ví dụ cơ bản về ==
Đối với == thì toán tử này được sử dụng để so sánh xem chuỗi A có bằng chuỗi B hay không, nhưng toán tử này so sánh dựa trên tiêu chí gì có phải cứ 2 chuỗi giống hệt nhau là = nhau hay không cùng xem ví dụ sau:
String x = "Hello World";
String y = "Hello World";
String z = new String("Hello World");
System.out.println(x==y); // kết quả trả về là true
System.out.println(x==z); // kết quả trả về là false
Ở ví dụ trên rõ ràng chúng ta đều thấy rằng cả 3 biến này đang đều có chung 1 giá trị là "Hello World"
nhưng sao khi so sánh x == y
kết quả trả về lại là true còn x == z
kết quả trả về lại là false. Đơn giản là vì ở đây toán tử == không so sánh 2 biến chuỗi về mặt giá trị mà nó đang so sánh 2 chuỗi này dựa trên tiêu chí là vùng nhớ.
Chúng ta cùng xem xét lại hình vẽ sau:
Như mình đã giải thích ở trên do x
và y
đang sử dụng String literal nên nó sẽ cùng sử dụng chung một ô nhớ trong String Pool nên kết quả trả về của phép so sánh x == y
sẽ là true
.
Còn đối với biến z do sử dụng từ khóa new nên nó sẽ được lưu trữ ở bộ nhớ Heap vậy nên x
và z
đang sử dụng 2 ô nhớ khác nhau nên kết quả trả về của x == z
sẽ là false.
Một ví dụ khác về ==
Nhưng đó là trong trường hợp đơn giản, chúng ta thử xem xét một trường hợp phức tạp hơn chút:
String a = "Hello";
String b = "World";
String c = a + " " + b;
String y = "Hello World";
System.out.println(c==y);
Theo các bạn 2 phép so sánh trên sẽ trả về kết quả là gì có còn là true
nữa hay không? Rất tiếc là ở ví dụ này thì kết quả trả về sẽ là false
.
Ở trên mình đang biểu diễn c
và y
lưu trữ giá trị “Hello World” ở 2 ô nhớ khác nhau, vì sao lại như vậy?
Lý do là vì hiện tại biến chuỗi c
của chúng ta đang được gán bằng biến chuỗi a
+ biến chuỗi b
nên là nó không thể đảm bảo rằng trong quá trình runtime giá trị của a
và b
sẽ không bị thay đổi nếu chẳng may tại một thời điểm nào đó biến a của chúng ta không còn giá trị "Hello"
nữa thì sao? Nếu lúc đó 2 biến c
và y
cùng lưu chung 1 ô nhớ có phải sẽ làm ảnh hưởng đến logic của biến y
hay không? Vì vậy nên Java hiểu là để không xảy ra vấn đề hi hữu đó nó cần lưu c
và y
vào 2 ô nhớ khác nhau. Do đó c == y
sẽ trả về giá trị là false
.
Vậy làm thế nào để phép so sánh c == y
có thể trả về giá trị = true
. Chúng ta có thể xử lý bằng cách là thêm từ khóa final
vào các biến chuỗi a
và biến chuỗi b
để những biến này trở thành 1 hằng chuỗi có giá trị không đổi. Khi đó kết quả trả về sẽ là true
.
final String a = "Hello";
final String b = "World";
String c = a + " " + b;
String y = "Hello World";
System.out.println(c==y);
4.2. Sử dụng phương thức equals() để so sánh chuỗi
Nếu như toán tử ==
so sánh 2 chuỗi dựa trên việc 2 chuỗi đó có lưu chung ô nhớ hay không thì phương thức equals()
sẽ so sánh 2 chuỗi đó dựa trên giá trị của chúng. Nên nếu quay lại ví dụ trên và sửa thành như sau chúng ta sẽ thu được kết quả là true
:
String x = "Hello World";
String y = "Hello World";
String z = new String("Hello World");
System.out.println(x.equals(y)); // kết quả trả về là true
System.out.println(x.equals(z)); // kết quả trả về là true
5. Kết luận
Vậy là chúng ta đã cùng nhau tìm hiểu được về String Pool là gì và nó hoạt động như thế nào? Hi vọng bài viết sẽ có ích với các bạn ^^