Khi nào chúng ta nên sử dụng phương thức intern của String trên chuỗi chữ


187

Theo String # intern () , internphương thức được cho là trả về Chuỗi từ nhóm Chuỗi nếu Chuỗi được tìm thấy trong nhóm Chuỗi, nếu không, một đối tượng chuỗi mới sẽ được thêm vào trong chuỗi Chuỗi và tham chiếu của Chuỗi này được trả về.

Vì vậy, tôi đã thử điều này:

String s1 = "Rakesh";
String s2 = "Rakesh";
String s3 = "Rakesh".intern();

if ( s1 == s2 ){
    System.out.println("s1 and s2 are same");  // 1.
}

if ( s1 == s3 ){
    System.out.println("s1 and s3 are same" );  // 2.
}

Tôi đã hy vọng rằng nó s1 and s3 are samesẽ được in như s3 được thực tập, và s1 and s2 are samesẽ không được in. Nhưng kết quả là: cả hai dòng được in. Vì vậy, điều đó có nghĩa là, mặc định các hằng chuỗi được thực hiện. Nhưng nếu nó là như vậy, tại sao chúng ta cần internphương pháp? Nói cách khác khi nào chúng ta nên sử dụng phương pháp này?


14
Javadoc mà bạn đã liên kết cũng nêu rõ "Tất cả các chuỗi ký tự và các biểu thức hằng có giá trị chuỗi được thực hiện."
Jorn


1
không phải là một bản sao chính xác ..
Bozho

1
@Jorn: đúng vậy Vậy tại sao chúng ta có internphương pháp công khai. Chúng ta không nên có internphương pháp riêng tư, để không ai có thể truy cập vào nó. Hoặc có bất kỳ mục đích của phương pháp này?
Rakesh Juyal

2
@RakeshJuyal: Phương thức intern được định nghĩa trên một kiểu chuỗi có thể là chuỗi ký tự hoặc biến. Làm thế nào bạn sẽ thực tập một biến nếu phương thức là riêng tư?
bulkalex

Câu trả lời:


230

Java tự động thực tập chuỗi ký tự. Điều này có nghĩa là trong nhiều trường hợp, toán tử == dường như hoạt động với Chuỗi theo cách tương tự như đối với ints hoặc các giá trị nguyên thủy khác.

Vì thực tập là tự động cho chuỗi ký tự Chuỗi, nên intern()phương thức này được sử dụng trên Chuỗi được xây dựng vớinew String()

Sử dụng ví dụ của bạn:

String s1 = "Rakesh";
String s2 = "Rakesh";
String s3 = "Rakesh".intern();
String s4 = new String("Rakesh");
String s5 = new String("Rakesh").intern();

if ( s1 == s2 ){
    System.out.println("s1 and s2 are same");  // 1.
}

if ( s1 == s3 ){
    System.out.println("s1 and s3 are same" );  // 2.
}

if ( s1 == s4 ){
    System.out.println("s1 and s4 are same" );  // 3.
}

if ( s1 == s5 ){
    System.out.println("s1 and s5 are same" );  // 4.
}

sẽ trở lại:

s1 and s2 are same
s1 and s3 are same
s1 and s5 are same

Trong tất cả các trường hợp ngoài s4biến, một giá trị được tạo rõ ràng bằng newtoán tử và internphương thức không được sử dụng trên kết quả của nó, đó là một trường hợp bất biến duy nhất được trả về nhóm hằng số chuỗi của JVM .

Tham khảo "Chuỗi bình đẳng và thực tập" của JavaT kỹ thuật để biết thêm thông tin.


Tôi giả định rằng Java tự động thực hiện chuỗi ký tự chuỗi cho mục đích tối ưu hóa. Nó có thể làm điều này một cách an toàn chỉ vì String là bất biến, đúng không?
styfle

Mới sử dụng Java (tôi đến từ thế giới C # .NET) và đôi khi tôi thấy trong một dự án kế thừa Java "" .i INTERN () vì vậy nếu tôi hiểu chính xác thì đây cũng là "vô nghĩa" đối với các chuỗi rỗng.
hfrmobile

4
@Miguel Giải thích hay, Câu hỏi của tôi là làm thế nào đối tượng có thể được tạo ở đây trong ví dụ của bạn. Đây là Giả định của tôi: String s1 = "Rakesh"; OB1 đầu tiên String s4 = new String("Rakesh");OB2 Thứ hai Vì vậy, phần còn lại của (s2, s3, s5) cùng một đối tượng (OB1) được tạo trong 'chuỗi Pool' Vì vậy tôi có thể nói rằng .intern()phương thức đó được sử dụng để ngăn chặn tạo đối tượng mới nếu có cùng chuỗi trong string poolIf giả định của tôi là sai vì vậy hãy cho tôi hướng.
Hybris Trợ giúp

1
Liên kết
JavaT kỹ thuật


20

Trong một dự án gần đây, một số cấu trúc dữ liệu khổng lồ đã được thiết lập với dữ liệu được đọc từ cơ sở dữ liệu (và do đó không phải là hằng chuỗi / chữ) nhưng với số lượng trùng lặp rất lớn. Đó là một ứng dụng ngân hàng, và những thứ như tên của một tập đoàn khiêm tốn (có thể 100 hoặc 200) xuất hiện ở khắp mọi nơi. Các cấu trúc dữ liệu đã rất lớn và nếu tất cả các tên corp đó là các đối tượng duy nhất thì chúng sẽ bị tràn bộ nhớ. Thay vào đó, tất cả các cấu trúc dữ liệu có tham chiếu đến cùng các đối tượng Chuỗi 100 hoặc 200, do đó tiết kiệm rất nhiều không gian.

Một lợi thế nhỏ khác của Chuỗi được thực hiện là ==có thể được sử dụng (thành công!) Để so sánh Chuỗi nếu tất cả các chuỗi liên quan được đảm bảo được thực hiện. Ngoài cú pháp tinh gọn hơn, đây cũng là một cải tiến hiệu suất. Nhưng như những người khác đã chỉ ra, thực hiện điều này có một rủi ro lớn trong việc đưa ra các lỗi lập trình, vì vậy điều này chỉ nên được thực hiện như một biện pháp khác biệt của biện pháp cuối cùng.

Nhược điểm là việc thực hiện một Chuỗi mất nhiều thời gian hơn là chỉ đơn giản là ném nó vào heap và không gian cho các Chuỗi được thực hiện có thể bị giới hạn, tùy thuộc vào việc triển khai Java. Hoàn thành tốt nhất khi bạn xử lý một số Chuỗi hợp lý đã biết với nhiều lần trùng lặp.


@ The downside is that interning a String takes more time than simply throwing it on the heap, and that the space for interned Strings may be limitedngay cả khi bạn không sử dụng phương thức intern cho hằng chuỗi, nó sẽ được tự động thực hiện.
Rakesh Juyal

2
@Rakesh: Thường không có nhiều hằng chuỗi trong bất kỳ lớp cụ thể nào, vì vậy nó không phải là vấn đề về không gian / thời gian với các hằng số.
David Rodríguez - dribeas

Có, nhận xét của Rakesh không áp dụng vì các chuỗi thực tập chỉ được thực hiện (rõ ràng) với các Chuỗi được "tạo" bằng cách nào đó, có thể bằng thao tác nội bộ hoặc bằng cách truy xuất từ ​​cơ sở dữ liệu. Với hằng số chúng ta không có lựa chọn.
Carl Smotricz

2
+1. Tôi nghĩ rằng đây là một ví dụ tốt khi thực tập có ý nghĩa. Tôi không đồng ý về ==chuỗi mặc dù.
Alexander Pogrebnyak

1
Từ Java 7 trở đi, "Chuỗi nhóm" được triển khai thành không gian heap, do đó, nó có được tất cả các lợi thế của việc lưu trữ thực tập, thu gom rác và kích thước của nó không bị giới hạn, nó có thể được tăng lên đến kích thước heap. (Bạn sẽ không bao giờ cần nhiều đến thế bộ nhớ cho chuỗi)
Anil Uttani

15

Tôi muốn thêm 2 xu của mình vào việc sử dụng ==với các chuỗi được liên kết.

Điều đầu tiên String.equalslàm là this==object.

Vì vậy, mặc dù có một số mức tăng hiệu suất rất nhỏ (bạn không gọi một phương thức), từ quan điểm của người bảo trì sử dụng ==là một cơn ác mộng, bởi vì một số chuỗi được thực hiện có xu hướng không được thực hiện.

Vì vậy, tôi đề nghị không nên dựa vào trường hợp đặc biệt của ==các chuỗi được thực hiện, mà luôn luôn sử dụng equalsnhư dự định của Gosling.

EDIT: thực tập trở thành không thực tập:

V1.0
public class MyClass
{
  private String reference_val;

  ...

  private boolean hasReferenceVal ( final String[] strings )
  {
    for ( String s : strings )
    {
      if ( s == reference_val )
      {
        return true;
      }
    }

    return false;
  }

  private void makeCall ( )
  {
     final String[] interned_strings =  { ... init with interned values ... };

     if ( hasReference( interned_strings ) )
     {
        ...
     }
  }
}

Trong phiên bản 2.0, người bảo trì đã quyết định hasReferenceValcông khai, mà không đi sâu vào chi tiết rằng nó mong đợi một chuỗi các chuỗi được thực hiện.

V2.0
public class MyClass
{
  private String reference_val;

  ...

  public boolean hasReferenceVal ( final String[] strings )
  {
    for ( String s : strings )
    {
      if ( s == reference_val )
      {
        return true;
      }
    }

    return false;
  }

  private void makeCall ( )
  {
     final String[] interned_strings =  { ... init with interned values ... };

     if ( hasReference( interned_strings ) )
     {
        ...
     }
  }
}

Bây giờ bạn có một lỗi, có thể rất khó tìm, bởi vì trong phần lớn các trường hợp mảng chứa các giá trị bằng chữ và đôi khi một chuỗi không phải là chữ được sử dụng. Nếu equalsđược sử dụng thay vì ==sau đó hasReferenceValvẫn sẽ tiếp tục làm việc. Một lần nữa, hiệu suất đạt được là rất nhỏ, nhưng chi phí bảo trì cao.


"một số chuỗi thực tập có xu hướng trở thành không thực tập." wow, đó sẽ là ... lạ. Bạn có thể trích dẫn một tài liệu tham khảo, xin vui lòng?
Carl Smotricz

2
OK, tôi nghĩ rằng bạn đang đề cập đến String thực sự lang thang ra khỏi nhóm thực tập và lên đống nhờ vào phép thuật trong JVM. Điều bạn đang nói là == làm cho các lớp lập trình lỗi nhất định có nhiều khả năng hơn.
Carl Smotricz

"Vì vậy, tôi khuyên bạn không nên dựa vào trường hợp đặc biệt == cho các chuỗi được liên kết, nhưng luôn luôn sử dụng bằng như ý định của Gosling." Bạn có một trích dẫn hoặc nhận xét trực tiếp từ Gosling nói điều này? Nếu đó là lý do tại sao anh ta thậm chí còn bận tâm đặt intern () và việc sử dụng == trong ngôn ngữ?

1
intern không tốt cho so sánh trực tiếp (==), mặc dù nó hoạt động nếu cả hai chuỗi được thực tập. thật tuyệt vời khi hạ thấp tổng bộ nhớ được sử dụng: khi cùng một chuỗi được sử dụng ở hơn 1 vị trí.
tgkprog

12

Chuỗi ký tự và hằng được mặc định theo mặc định. Đó là, "foo" == "foo"(được khai báo bằng chuỗi ký tự), nhưng new String("foo") != new String("foo").


4
Vì vậy, câu hỏi là khi nào chúng ta nên sử dụng intern,
Rakesh Juyal

đã được trỏ đến stackoverflow.com/questions/1833581/when-to-use-itern , và một số câu hỏi khác, một số câu hỏi từ ngày hôm qua.
Bozho

Hãy cho tôi biết nếu sự hiểu biết của tôi cho tuyên bố này : String literals and constants are interned by default, là chính xác. new String("foo")-> Ở đây, một chuỗi "foo" theo nghĩa đen được tạo trong nhóm Chuỗi và một trong heap, do đó tổng số 2 đối tượng được tạo.
dkb

8

Tìm hiểu Java String Intern - một lần cho tất cả

Các chuỗi trong java là các đối tượng bất biến theo thiết kế. Do đó, hai đối tượng chuỗi thậm chí có cùng giá trị sẽ là các đối tượng khác nhau theo mặc định. Tuy nhiên, nếu chúng ta muốn lưu bộ nhớ, chúng ta có thể chỉ ra sử dụng cùng một bộ nhớ theo một khái niệm gọi là chuỗi intern.

Các quy tắc dưới đây sẽ giúp bạn hiểu khái niệm này theo các thuật ngữ rõ ràng:

  1. Lớp chuỗi duy trì một nhóm thực tập ban đầu trống. Nhóm này phải đảm bảo chứa các đối tượng chuỗi chỉ có các giá trị duy nhất.
  2. Tất cả các chuỗi ký tự có cùng giá trị phải được coi là cùng một đối tượng vị trí bộ nhớ vì chúng có khái niệm khác biệt. Do đó, tất cả các chữ như vậy có cùng giá trị sẽ tạo một mục duy nhất trong nhóm nội bộ và sẽ tham chiếu đến cùng một vị trí bộ nhớ.
  3. Ghép từ hai hoặc nhiều chữ cũng là một nghĩa đen. (Do đó, quy tắc số 2 sẽ được áp dụng cho họ)
  4. Mỗi chuỗi được tạo dưới dạng đối tượng (nghĩa là bằng bất kỳ phương thức nào khác ngoại trừ bằng chữ) sẽ có các vị trí bộ nhớ khác nhau và sẽ không thực hiện bất kỳ mục nhập nào trong nhóm thực tập
  5. Sự kết hợp của những chữ có nghĩa đen sẽ làm cho một chữ không có nghĩa đen. Do đó, đối tượng kết quả sẽ có một vị trí bộ nhớ mới và sẽ KHÔNG tạo một mục trong nhóm thực tập.
  6. Gọi phương thức intern trên một đối tượng chuỗi, hoặc tạo một đối tượng mới vào nhóm intern-return hoặc trả về một đối tượng hiện có từ nhóm có cùng giá trị. Lệnh gọi trên bất kỳ đối tượng nào không nằm trong nhóm thực tập, KHÔNG di chuyển đối tượng đến nhóm. Nó thay vì tạo ra một đối tượng khác đi vào hồ bơi.

Thí dụ:

String s1=new String (“abc”);
String s2=new String (“abc”);
If (s1==s2)  //would return false  by rule #4
If (“abc == a”+”bc )  //would return true by rules #2 and #3
If (“abc == s1 )  //would return false  by rules #1,2 and #4
If (“abc == s1.intern() )  //would return true  by rules #1,2,4 and #6
If ( s1 == s2.intern() )      //wound return false by rules #1,4, and #6

Lưu ý: Các trường hợp động lực cho chuỗi thực tập không được thảo luận ở đây. Tuy nhiên, tiết kiệm bộ nhớ chắc chắn sẽ là một trong những mục tiêu chính.


Cảm ơn bạn vì số 3, tôi không biết :)
kaay

4

bạn nên tạo ra hai khoảng thời gian là thời gian biên dịch và thời gian chạy. Ví dụ:

//example 1 
"test" == "test" // --> true 
"test" == "te" + "st" // --> true

//example 2 
"test" == "!test".substring(1) // --> false
"test" == "!test".substring(1).intern() // --> true

trong một mặt, trong ví dụ 1, chúng tôi thấy kết quả đều trả về đúng, bởi vì trong thời gian biên dịch, jvm sẽ đặt "test" vào nhóm các chuỗi ký tự, nếu jvm tìm thấy "test" tồn tại, thì nó sẽ sử dụng chuỗi tồn tại, trong ví dụ 1, các chuỗi "test" đều được trỏ đến cùng một địa chỉ bộ nhớ, vì vậy ví dụ 1 sẽ trả về true. mặt khác, trong ví dụ 2, phương thức chuỗi con () thực thi trong thời gian chạy, trong trường hợp "test" == "! test" .sub chuỗi (1), nhóm sẽ tạo hai đối tượng chuỗi, " test "và"! test ", vì vậy chúng là các đối tượng tham chiếu khác nhau, vì vậy trường hợp này sẽ trả về false, trong trường hợp" test "=="! test ".sub chuỗi (1) .i INTERN (), phương thức intern ( ) sẽ đặt ""! test ".sub chuỗi (1)" vào nhóm các chuỗi ký tự,


3

http://en.wikipedia.org/wiki/String_iterning

thực tập chuỗi là một phương pháp chỉ lưu trữ một bản sao của mỗi giá trị chuỗi riêng biệt, phải là bất biến. Chuỗi thực tập làm cho một số tác vụ xử lý chuỗi hiệu quả hơn về thời gian hoặc không gian với chi phí đòi hỏi nhiều thời gian hơn khi chuỗi được tạo hoặc được thực hiện. Các giá trị riêng biệt được lưu trữ trong một nhóm thực tập chuỗi.


2

Chuỗi liên kết tránh các chuỗi trùng lặp. Thực tập giúp tiết kiệm RAM với chi phí nhiều thời gian CPU hơn để phát hiện và thay thế các Chuỗi trùng lặp. Chỉ có một bản sao của mỗi Chuỗi đã được thực hiện, bất kể có bao nhiêu tài liệu tham khảo trỏ đến nó. Vì Chuỗi là bất biến, nếu hai phương thức khác nhau sử dụng cùng một Chuỗi, chúng có thể chia sẻ một bản sao của cùng một Chuỗi. Quá trình chuyển đổi các chuỗi trùng lặp thành các chuỗi được chia sẻ được gọi là interning.String.i INTERN () cung cấp cho bạn địa chỉ của Chuỗi chính chính. Bạn có thể so sánh các chuỗi đã thực hiện với đơn giản == (so sánh các con trỏ) thay vì bằngtrong đó so sánh các ký tự của Chuỗi từng cái một. Bởi vì Chuỗi là bất biến, quá trình thực tập có thể tiết kiệm không gian hơn nữa, ví dụ, bằng cách không tạo một chuỗi ký tự riêng cho "pot" khi nó tồn tại dưới dạng một chuỗi con của một số nghĩa đen khác như "hà mã".

Để xem thêm http://mindprod.com/jgloss/i INTERNed.html


2
String s1 = "Anish";
        String s2 = "Anish";

        String s3 = new String("Anish");

        /*
         * When the intern method is invoked, if the pool already contains a
         * string equal to this String object as determined by the
         * method, then the string from the pool is
         * returned. Otherwise, this String object is added to the
         * pool and a reference to this String object is returned.
         */
        String s4 = new String("Anish").intern();
        if (s1 == s2) {
            System.out.println("s1 and s2 are same");
        }

        if (s1 == s3) {
            System.out.println("s1 and s3 are same");
        }

        if (s1 == s4) {
            System.out.println("s1 and s4 are same");
        }

ĐẦU RA

s1 and s2 are same
s1 and s4 are same

2
String p1 = "example";
String p2 = "example";
String p3 = "example".intern();
String p4 = p2.intern();
String p5 = new String(p3);
String p6 = new String("example");
String p7 = p6.intern();

if (p1 == p2)
    System.out.println("p1 and p2 are the same");
if (p1 == p3)
    System.out.println("p1 and p3 are the same");
if (p1 == p4)
    System.out.println("p1 and p4 are the same");
if (p1 == p5)
    System.out.println("p1 and p5 are the same");
if (p1 == p6)
    System.out.println("p1 and p6 are the same");
if (p1 == p6.intern())
    System.out.println("p1 and p6 are the same when intern is used");
if (p1 == p7)
    System.out.println("p1 and p7 are the same");

Khi hai chuỗi được tạo độc lập, intern()cho phép bạn so sánh chúng và nó cũng giúp bạn tạo tham chiếu trong nhóm chuỗi nếu tham chiếu không tồn tại trước đó.

Khi bạn sử dụng String s = new String(hi), java tạo một thể hiện mới của chuỗi, nhưng khi bạn sử dụngString s = "hi" , java sẽ kiểm tra xem có một thể hiện của từ "hi" trong mã hay không và nếu nó tồn tại, nó chỉ trả về tham chiếu.

Vì việc so sánh các chuỗi dựa trên tham chiếu, intern()giúp bạn tạo một tham chiếu và cho phép bạn so sánh các nội dung của các chuỗi.

Khi bạn sử dụng intern()trong mã, nó sẽ xóa khoảng trống được sử dụng bởi chuỗi tham chiếu đến cùng một đối tượng và chỉ trả về tham chiếu của cùng một đối tượng đã có trong bộ nhớ.

Nhưng trong trường hợp p5 khi bạn đang sử dụng:

String p5 = new String(p3);

Chỉ nội dung của p3 được sao chép và p5 được tạo mới. Vì vậy, nó không được thực tập .

Vì vậy, đầu ra sẽ là:

p1 and p2 are the same
p1 and p3 are the same
p1 and p4 are the same
p1 and p6 are the same when intern is used
p1 and p7 are the same

2
    public static void main(String[] args) {
    // TODO Auto-generated method stub
    String s1 = "test";
    String s2 = new String("test");
    System.out.println(s1==s2);              //false
    System.out.println(s1==s2.intern());    //true --> because this time compiler is checking from string constant pool.
}

1

Phương thức chuỗi intern () được sử dụng để tạo một bản sao chính xác của đối tượng chuỗi heap trong nhóm hằng số chuỗi. Các đối tượng chuỗi trong nhóm hằng chuỗi được tự động thực hiện nhưng các đối tượng chuỗi trong heap thì không. Công dụng chính của việc tạo intern là để tiết kiệm không gian bộ nhớ và thực hiện so sánh nhanh hơn các đối tượng chuỗi.

Nguồn: Chuỗi thực tập trong java là gì?


1

Như bạn đã nói, intern()phương thức chuỗi đó trước tiên sẽ tìm thấy từ nhóm Chuỗi, nếu tìm thấy, thì nó sẽ trả về đối tượng trỏ đến đó hoặc sẽ thêm một Chuỗi mới vào nhóm.

    String s1 = "Hello";
    String s2 = "Hello";
    String s3 = "Hello".intern();
    String s4 = new String("Hello");

    System.out.println(s1 == s2);//true
    System.out.println(s1 == s3);//true
    System.out.println(s1 == s4.intern());//true

Các s1s2hai đối tượng trỏ đến String hồ bơi "Hello", và sử dụng "Hello".intern()sẽ thấy rằng s1s2. Vì vậy, "s1 == s3"trả về đúng, cũng như cho s3.intern().


Điều này không thực sự cung cấp nhiều thông tin mới. Đã có một câu trả lời bị loại trừ.
Alexander

0

Bằng cách sử dụng tham chiếu đối tượng heap nếu chúng ta muốn có được tham chiếu đối tượng nhóm hằng số chuỗi tương ứng , thì chúng ta nên đi intern ()

String s1 = new String("Rakesh");
String s2 = s1.intern();
String s3 = "Rakesh";

System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true

Xem ảnh nhập mô tả hình ảnh ở đây

Bước 1: Đối tượng với dữ liệu 'Rakesh' được tạo trong nhóm liên tục heap và chuỗi. Ngoài ra s1 luôn luôn trỏ đến đối tượng heap.

Bước 2: Bằng cách sử dụng tham chiếu đối tượng heap s1, chúng tôi đang cố gắng để có được chuỗi đối tượng nhóm hằng số tham chiếu s2 tương ứng, sử dụng intern ()

Bước 3: Cố ý tạo một đối tượng có dữ liệu 'Rakesh' trong nhóm hằng chuỗi, được tham chiếu theo tên s3

Như toán tử "==" có nghĩa là để so sánh tham chiếu.

Bắt sai cho s1 == s2

Bắt đúng với s2 == s3

Hy vọng điều này giúp đỡ!!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.