2010-09-27 38 views
62

Tôi đang nhầm lẫn về StringPool trong Java. Tôi đã xem qua điều này trong khi đọc chương String trong Java. Xin hãy giúp tôi hiểu, trong điều khoản của giáo dân, những gì StringPool thực sự làm.Chuỗi nhóm trong Java là gì?

Trả lời

114

này in true (mặc dù chúng ta không sử dụng phương pháp equals: cách chính xác để so sánh chuỗi)

String s = "a" + "bc"; 
    String t = "ab" + "c"; 
    System.out.println(s == t); 

Khi trình biên dịch tối ưu hóa chuỗi literals của bạn, nó thấy rằng cả hai st có cùng giá trị và do đó bạn chỉ cần một đối tượng chuỗi. Nó an toàn vì String là bất biến trong Java.
Kết quả là cả hai số st trỏ đến cùng một đối tượng và một số bộ nhớ nhỏ được lưu.

Tên 'chuỗi nhóm' xuất phát từ ý tưởng rằng tất cả chuỗi đã xác định được lưu trữ trong một số 'nhóm' và trước khi tạo trình biên dịch đối tượng String mới kiểm tra xem chuỗi đó đã được xác định chưa.

+2

Java có các loại trình bao bọc cho các kiểu nguyên thủy và các lớp đó là không thể thay đổi được .. Giống như Integer, Charecter, và Double .... vv. Họ cũng có một hồ bơi để tiết kiệm bộ nhớ ?? Nếu không, những gì là đặc biệt về String để có một hồ bơi? –

+2

@PunithRaj Tôi thực sự không chắc chắn! Tôi nghi ngờ nó, tuy nhiên. int, ví dụ, chỉ có 4 byte, vì vậy bạn không phải tiết kiệm được nhiều bằng cách có hai điểm của Integer đến cùng một vị trí trong bộ nhớ. Ngược lại, phải duy trì một 'số nguyên hồ bơi' để phát hiện các giá trị lặp đi lặp lại có khả năng lãng phí nhiều bộ nhớ hơn bạn sẽ tiết kiệm được bằng cách tránh các giá trị trùng lặp. –

+1

@PunithRaj String không phải là kiểu dữ liệu nguyên thủy (techinically/implementation wise) và String không có lớp bao bọc như cách char/int làm. – user3240361

37

Tôi không nghĩ nó thực sự làm được gì nhiều, có vẻ như nó chỉ là một bộ nhớ đệm cho các chuỗi ký tự. Nếu bạn có nhiều chuỗi giá trị giống nhau, tất cả chúng sẽ trỏ đến cùng một chuỗi ký tự trong nhóm chuỗi.

String s1 = "Arul"; //case 1 
String s2 = "Arul"; //case 2 

Trong trường hợp 1, chữ S1 được tạo mới và được giữ trong hồ bơi. Nhưng trong trường hợp 2, s2 theo nghĩa đen tham chiếu s1, nó sẽ không tạo ra s1 mới.

if(s1 == s2) System.out.println("equal"); //Prints equal. 

String n1 = new String("Arul"); 
String n2 = new String("Arul"); 
if(n1 == n2) System.out.println("equal"); //No output. 

http://p2p.wrox.com/java-espanol/29312-string-pooling.html

14

Khi tải lớp JVM, hoặc nếu không thấy một chuỗi chữ, hoặc một số chuỗi sa đang intern, nó bổ sung thêm chuỗi vào một bảng tra cứu chủ yếu-ẩn mà có một bản sao của mỗi ví dụ chuỗi. Nếu một bản sao khác được thêm vào, thời gian chạy sẽ sắp xếp nó sao cho tất cả các chữ cái tham chiếu đến cùng một đối tượng chuỗi. Điều này được gọi là "interning". Nếu bạn nói điều gì đó giống như

String s = "test"; 
return (s == "test"); 

nó sẽ trả lại true, vì thử nghiệm đầu tiên và thứ hai thực sự là cùng một đối tượng. So sánh chuỗi nội bộ theo cách này có thể nhiều, nhiều hơn nhanh hơn String.equals, vì có một so sánh tham chiếu đơn lẻ thay vì một loạt các so sánh char.

Bạn có thể thêm chuỗi vào hồ bơi bằng cách gọi String.intern(), sẽ cung cấp cho bạn phiên bản chuỗi được gộp chung (có thể là cùng chuỗi mà bạn đang thực tập, nhưng bạn sẽ điên khùng dựa vào đó - - bạn thường không thể chắc chắn chính xác mã nào đã được tải và chạy lên đến bây giờ và thực hiện cùng một chuỗi). Phiên bản gộp (chuỗi được trả về từ intern) sẽ bằng bất kỳ chữ nào giống hệt nhau. Ví dụ:

String s1 = "test"; 
String s2 = new String("test"); // "new String" guarantees a different object 

System.out.println(s1 == s2); // should print "false" 

s2 = s2.intern(); 
System.out.println(s1 == s2); // should print "true" 
+0

Tôi thực sự không nghĩ rằng nó được thực hiện tại thời gian chạy. Thậm chí các chuỗi đơn giản nhất được xây dựng với các phương thức sẽ không được gộp lại. Ví dụ., ví dụ từ câu trả lời của tôi sẽ không hoạt động nếu tôi sử dụng _concat_ thay vì _ + _ –

+1

@Nikita: Đó là vì 'concat' không thể dễ dàng được tối ưu hóa. Các chuỗi được catted cùng với '+' có thể được định trước bởi bất kỳ trình biên dịch tự tôn trọng nào, bởi vì giá trị không bao giờ thay đổi. Nhưng trình biên dịch không thể đoán được liệu một hàm sẽ trả về cùng một giá trị mọi lúc (một số không), vì vậy nó sẽ không thử. Nếu bạn sử dụng 'concat' thay thế trong ví dụ của bạn," ab "," c "," a "và" bc "sẽ được thực tập, nhưng" abc "sẽ không (vì nó không phải là chữ, và mã của bạn không có t 'intern' nó). Tuy nhiên, với '+' một trình biên dịch phong nha sẽ thấy rằng cả hai chuỗi là "abc" và biên dịch nó. – cHao

+1

Việc thực tập sẽ * có * được thực hiện trong thời gian chạy, vì (1) nhóm luôn bắt đầu trống, và (2) hai lớp khác nhau có thể có "abc" trong chúng. Nếu thực tập là một điều biên dịch và cả hai lớp đã được nạp, thì cuối cùng sẽ có hai "abc" trong nhóm chuỗi, điều này sẽ đánh bại toàn bộ mục đích của nhóm chuỗi. – cHao

16

Hãy bắt đầu với một trích dẫn từ các máy đặc tả ảo:

tải của một lớp hoặc giao diện có chứa một chuỗi đen có thể tạo ra một đối tượng String mới (§2.4.8) đại diện cho chữ đó. Điều này có thể không xảy ra nếu một đối tượng String đã được tạo ra để đại diện cho một lần xuất hiện trước đó của chữ đó, hoặc nếu phương thức String.intern đã được gọi trên một đối tượng String biểu diễn cùng một chuỗi như chữ.

Điều này có thể không xảy ra - Đây là gợi ý, có điều gì đó đặc biệt về các đối tượng String. Thông thường, gọi một hàm tạo sẽ luôn tạo một phiên bản mới của lớp. Đây không phải là trường hợp với Strings, đặc biệt là khi các đối tượng String được 'tạo ra' với các chữ. Các String này được lưu trữ trong một kho lưu trữ toàn cục (pool) - hoặc ít nhất các tham chiếu được lưu giữ trong một pool, và bất cứ khi nào một thể hiện mới của một Strings đã biết là cần thiết, vm trả về một tham chiếu đến đối tượng từ nhóm. Trong mã giả, nó có thể đi như thế:

1: a := "one" 
    --> if(pool[hash("one")] == null) // true 
      pool[hash("one") --> "one"] 
     return pool[hash("one")] 

2: b := "one" 
    --> if(pool[hash("one")] == null) // false, "one" already in pool 
     pool[hash("one") --> "one"] 
     return pool[hash("one")] 

Vì vậy, trong trường hợp này, các biến ab tài liệu tham khảo để giữ cùng đối tượng. Trong trường hợp này, chúng tôi có (a == b) && (a.equals(b)) == true.

Đây không phải là trường hợp nếu chúng tôi sử dụng các nhà xây dựng:

1: a := "one" 
2: b := new String("one") 

Một lần nữa, "one" được tạo ra trên hồ nhưng sau đó chúng ta tạo một đối tượng mới so với cùng theo nghĩa đen, và trong trường hợp này, nó dẫn đến (a == b) && (a.equals(b)) == false

Vì vậy, lý do tại sao chúng tôi có hồ bơi Chuỗi không? Các chuỗi và đặc biệt là các chuỗi ký tự String được sử dụng rộng rãi trong mã Java điển hình. Và họ là bất biến. Và không thay đổi được phép cache String để tiết kiệm bộ nhớ và tăng hiệu suất (ít nỗ lực để tạo ra, ít rác hơn để được thu thập).

Khi lập trình, chúng tôi không cần phải quan tâm nhiều về hồ String, miễn là chúng ta cần lưu ý:

  • (a == b) && (a.equals(b)) có thể true hoặc false (luôn sử dụng equals để so sánh Strings)
  • Không sử dụng sự phản chiếu để thay đổi sự ủng hộ char[] của một chuỗi (vì bạn không biết ai đang sử dụng chuỗi đó)
+0

Nếu bạn * quan tâm đến nhóm chuỗi, có khả năng tăng hiệu suất lớn trong các ứng dụng sử dụng một nhóm nhỏ các chuỗi rộng rãi, thường là các thẻ hoặc từ khóa. Khi các chuỗi được tập trung, so sánh sẽ trở thành một '==' duy nhất thay vì hàm gọi, hai cuộc gọi chiều dài() và chuỗi so sánh char có thể xảy ra với 'bằng'. – cHao

+0

@cHao Vì sự an toàn và nhất quán, bạn vẫn có thể sử dụng 'String.equals()' với chuỗi nội bộ, vì 'String.equals()' đầu tiên thực hiện một '==' so sánh – bcoughlan

+1

@bcoughlan: '==' là an toàn và nhất quán là 'bằng' - nó chỉ bị hiểu lầm. Những người sử dụng nó với các đối tượng nói chung rơi vào hai loại. Có những người không hiểu giá trị so với ngữ nghĩa định danh (và rằng == với các kiểu tham chiếu so sánh danh tính) - những người đó * nên * luôn sử dụng 'String.equals'. Sau đó, có những người hiểu, nhưng có ý thức * chọn * nhận dạng. Và nó hoạt động như một cách đáng tin cậy, miễn là bạn biết các vật thể của bạn đến từ đâu. Có một lý do '==' làm việc với các đối tượng - và đặc biệt, tại sao nó không chỉ gọi 'bằng'. – cHao