우리는 언제 String 리터럴에서 String의 인턴 메소드를 사용해야합니까?


187

에 따르면 문자열 # 인턴 () , intern메소드는 문자열이 문자열 풀에서 발견되는 경우, 그렇지 않으면 새로운 문자열 객체는 문자열 풀에 추가되고이 String의 참조가 반환, 문자열 수영장에서 문자열을 반환하기로되어있다.

그래서 나는 이것을 시도했다 :

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.
}

s1 and s3 are sames3이 억지로 인쇄되고 s1 and s2 are same인쇄되지 않을 것으로 예상했습니다 . 그러나 결과는 다음과 같습니다. 두 줄이 모두 인쇄됩니다. 따라서 기본적으로 문자열 상수가 인터 닝됩니다. 그러나 그렇다면 왜 intern방법이 필요 합니까? 다시 말해이 방법을 언제 사용해야합니까?


14
연결 한 Javadoc에도 "모든 리터럴 문자열과 문자열 값 상수 표현식이 인터 턴됩니다"라고 표시되어 있습니다.
Jorn December


1
정확한 복제본이 아닙니다.
Bozho 2009

1
@Jorn : 맞습니다. 왜 우리는 intern공개적인 방법 을 가지고 있습니까? intern아무도 접근 할 수 없도록 전용 메소드로 사용 해서는 안됩니다 . 아니면이 방법의 목적이 있습니까?
Rakesh Juyal

2
@RakeshJuyal : 인턴 메소드는 문자열 리터럴 또는 변수 일 수있는 문자열 유형에 정의됩니다. 메소드가 비공개 인 경우 어떻게 변수를 인턴 하시겠습니까?
bobbyalex

답변:


230

Java는 자동으로 문자열 리터럴을 인턴합니다. 이것은 많은 경우 == 연산자가 int 또는 다른 기본 값에서와 동일한 방식으로 문자열에서 작동하는 것으로 나타납니다.

interning은 String 리터럴에 대해 자동이기 때문에이 intern()메소드는로 구성된 String에서 사용됩니다.new String()

귀하의 예를 사용하여 :

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.
}

돌아올 것이다 :

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

s4변수를 제외한 모든 경우에 , new연산자를 사용하여 명시 적으로 작성된 값 과 intern그 결과에 메소드가 사용되지 않은 경우, JVM의 문자열 상수 풀로 리턴되는 변경 불가능한 단일 인스턴스입니다 .

자세한 정보는 JavaTechniques "문자열 동등성 및 인터 닝" 을 참조하십시오.


Java가 최적화를 위해 문자열 리터럴을 자동으로 인턴한다고 가정합니다. 문자열이 변경 불가능하기 때문에 안전하게 할 수 있습니까?
styfle

Java에 익숙하지 않으며 (C # .NET 세계 출신) Java 레거시 프로젝트 "".intern ()에서 가끔 볼 수 있으므로 빈 문자열에 대해서도 이것이 "논 센스"라는 것을 올바르게 이해하면.
hfrmobile

4
@Miguel 좋은 설명입니다. 제 질문은 당신이 예제에서 객체를 어떻게 만들 수 있는지입니다. 여기 내 가정이 있습니다 : String s1 = "Rakesh"; 첫 번째 OB1 String s4 = new String("Rakesh");두 번째 OB2 나머지 (s2, s3, s5)는 'string Pool'에서 생성 된 동일한 객체 (OB1)를 참조하므로 If .intern()에서 동일한 문자열을 사용할 수있는 string pool경우 새 객체를 만들지 못하게하는 데 사용할 수있는 방법을 말할 수 있습니까 내 가정이 잘못되었으므로 방향을 알려주십시오.
HybrisHelp

1
JavaTechniques 링크가 끊어졌습니다
SJuan76


20

최근 프로젝트에서 일부 거대한 데이터 구조는 데이터베이스에서 읽은 데이터 (따라서 문자열 상수 / 리터럴이 아님)로 설정되었지만 대량의 복제가 이루어졌습니다. 그것은 은행 응용 프로그램이었고, 적당한 세트 (100 또는 200) 회사의 이름과 같은 것들이 사방에 나타났습니다. 데이터 구조는 이미 커서 모든 회사 이름이 고유 한 개체라면 메모리가 넘 쳤을 것입니다. 대신, 모든 데이터 구조는 동일한 100 또는 200 문자열 오브젝트에 대한 참조를 가지므로 많은 공간을 절약합니다.

인터 링 된 문자열의 또 다른 작은 장점은 ==관련된 모든 문자열이 인터 닝되도록 보장 된 경우 (성공적으로!) 사용하여 문자열을 비교할 수 있다는 것입니다. 더 작은 구문 외에도 성능 향상입니다. 그러나 다른 사람들이 지적 했듯이이 작업을 수행하면 프로그래밍 오류가 발생할 위험이 커지므로 이는 최후의 수단에 대한 절망적 인 수단으로 만 수행해야합니다.

단점은 단순히 문자열을 힙에 던지는 것보다 문자열을 인턴하는 데 더 많은 시간이 걸리며 Java 구현에 따라 인턴 된 문자열의 공간이 제한 될 수 있다는 것입니다. 중복이 많은 알려진 합리적인 수의 문자열을 다룰 때 가장 좋습니다.


@ 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 limited문자열 상수에 인턴 방법을 사용하지 않더라도 자동으로 인턴됩니다.
Rakesh Juyal

2
@Rakesh : 주어진 클래스에는 일반적으로 문자열 상수가 많지 않으므로 상수가있는 공간 / 시간 문제는 아닙니다.
David Rodríguez-dribeas

그렇습니다. 내부 문자열이나 데이터베이스 등에서 검색하는 방식으로 "생성 된"문자열로만 인터 링 문자열을 수행하기 때문에 Rakesh의 의견은 적용되지 않습니다. 상수를 사용하면 선택의 여지가 없습니다.
Carl Smotricz

2
+1. 인턴이 이해 될 때 좋은 예라고 생각합니다. ==그래도 문자열에 동의하지 않습니다 .
Alexander Pogrebnyak

1
Java 7부터 "문자열 풀"은 힙 공간으로 구현되므로 인턴 저장, 가비지 콜렉션의 크기 및 제한되지 않은 힙 크기까지 증가시킬 수있는 모든 이점을 얻습니다. 문자열에 대한 메모리)
아닐 Uttani

15

==내부 문자열로 사용 하는 데 2 센트를 추가하고 싶습니다 .

가장 먼저하는 일은 String.equals입니다 this==object.

따라서 약간의 성능 향상 (메소드를 호출하지 않음)이 있지만 유지 보수 자의 관점에서 볼 때 ==일부 인턴 문자열은 인턴되지 않는 경향이 있기 때문에 악몽입니다.

따라서 나는 특별한 ==문자열 에 의존하지 말고 항상 equalsGosling이 의도 한대로 사용 하는 것이 좋습니다.

편집 : 인턴 비 인턴되기 :

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 ) )
     {
        ...
     }
  }
}

버전 2.0에서 관리자 hasReferenceVal는 인터 레 이닝 된 문자열의 배열을 기대한다는 것을 자세하게 설명하지 않고 공개 하기로 결정했습니다 .

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 ) )
     {
        ...
     }
  }
}

대부분의 경우 배열에 리터럴 값이 포함되어 있고 때로는 리터럴이 아닌 문자열이 사용되기 때문에 찾기가 매우 어려울 수있는 버그가 있습니다. 경우 equals대신 사용 된 ==hasReferenceVal아직이 작업을 계속할 것입니다. 다시 한번, 성능 향상은 미미하지만 유지 보수 비용은 높습니다.


"일부 인턴 문자열은 인턴되지 않는 경향이 있습니다." 와우, 그거 ... 이상해. 참고 문헌을 인용 할 수 있습니까?
Carl Smotricz 2009

2
자, JVM의 마술 덕분에 실제로 인턴 풀에서 힙에서 방황하는 문자열을 참조한다고 생각했습니다. 당신이 말하는 것은 == 특정 클래스의 프로그래머 오류를 더 가능성이 높다는 것입니다.
Carl Smotricz 2009

"따라서 내부 문자열에 특별한 경우 ==를 사용하지 말고 항상 Gosling이 의도 한 것과 동일하게 사용하는 것이 좋습니다." Gosling이 직접 인용하거나 언급 한 내용이 있습니까? 그렇다면 왜 그는 언어에 intern ()을 넣고 ==를 사용하는 것을 귀찮게 했습니까?

1
인턴은 두 문자열이 모두 인 터닝 되어도 직접 비교 (==)에 적합하지 않습니다. 사용 된 총 메모리를 낮추는 것이 좋습니다. 동일한 문자열이 두 곳 이상에서 사용될 때.
tgkprog

12

문자열 리터럴과 상수는 기본적으로 인턴됩니다. 즉, "foo" == "foo"(문자열 리터럴로 선언)이지만 new String("foo") != new String("foo").


4
우리가 사용한다 때, 질문은 intern,
케쉬 Juyal

이것은 stackoverflow.com/questions/1833581/when-to-use-intern 및 기타 여러 가지 질문 으로 지적되었습니다 .
Bozho

이 진술에 대한 나의 이해 String literals and constants are interned by default가 올바른지 알려주세요 . new String("foo")-> 여기에서 하나의 문자열 리터럴 "foo"가 문자열 풀에 작성되고 하나는 힙에 작성되므로 총 2 개의 오브젝트가 작성됩니다.
dkb

8

Java String Intern 배우기-한 번만

Java의 문자열은 의도적으로 변경할 수없는 객체입니다. 따라서 동일한 값을 가진 두 개의 문자열 객체는 기본적으로 다른 객체가됩니다. 그러나 메모리를 절약하려면 string intern이라는 개념으로 동일한 메모리를 사용하도록 지시 할 수 있습니다.

아래 규칙은 개념을 명확한 용어로 이해하는 데 도움이됩니다.

  1. 문자열 클래스는 초기에 비어있는 인턴 풀을 유지 관리합니다. 이 풀은 고유 한 값으로 만 문자열 객체를 포함해야합니다.
  2. 동일한 값을 가진 모든 문자열 리터럴은 다른 구별 개념이 없으므로 동일한 메모리 위치 객체로 간주해야합니다. 따라서 같은 값을 가진 모든 리터럴은 인턴 풀에서 단일 항목을 만들고 동일한 메모리 위치를 참조합니다.
  3. 둘 이상의 리터럴을 연결하는 것도 리터럴입니다. (따라서 규칙 # 2가 적용됩니다)
  4. 객체로 생성 된 (즉, 리터럴 이외의 다른 방법으로) 생성 된 각 문자열은 서로 다른 메모리 위치를 가지며 인턴 풀에 항목을 만들지 않습니다
  5. 리터럴이 아닌 리터럴을 연결하면 리터럴이되지 않습니다. 따라서 결과 객체는 새로운 메모리 위치를 가지며 인턴 풀에 항목을 만들지 않습니다.
  6. 문자열 객체에서 인턴 메서드를 호출하면 인턴 풀에 들어가는 새 객체를 만들거나 동일한 값을 가진 풀에서 기존 객체를 반환합니다. 인턴 풀에없는 오브젝트에 대한 호출은 오브젝트를 풀로 이동하지 않습니다. 오히려 수영장으로 들어가는 다른 객체를 만듭니다.

예:

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

참고 : 문자열 인턴에 대한 동기 부여 사례는 여기에서 다루지 않습니다. 그러나 메모리 절약은 분명히 주요 목표 중 하나입니다.


# 3 주셔서 감사합니다, 나는 몰랐다 :)
kaay

4

컴파일 시간과 런타임 시간 인 두 가지 기간을 만들어야합니다. 예를 들면 다음과 같습니다.

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

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

한 손으로, 예제 1에서, 컴파일 시간에 결과가 모두 true를 반환한다는 것을 알았습니다. jvm이 "test"가 존재하면 jvm이 리터럴 문자열의 풀에 "test"를 넣습니다. 예제 1에서 존재하는 것을 사용합니다. "테스트"문자열은 모두 동일한 메모리 주소를 가리 키므로 예제 1은 true를 반환합니다. 반면, 예제 2에서 substring ()의 메소드는 런타임 시간에 실행됩니다. "test"== "! test".substring (1)의 경우 풀은 두 개의 문자열 객체를 생성합니다. " test "와"! test "는 서로 다른 참조 객체이므로이 경우"test "=="! test ".substring (1) .intern ()의 경우 intern ( )는 ""! test ".substring (1)"을 리터럴 문자열 풀에 넣습니다.


3

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

문자열 인턴은 각각의 고유 한 문자열 값의 사본을 하나만 저장하는 방법으로 변경 불가능합니다. 문자열을 문자열로 만들면 문자열을 만들거나 묶을 때 더 많은 시간이 걸리면서 일부 문자열 처리 작업이 시간이나 공간 효율성이 향상됩니다. 고유 값은 문자열 인턴 풀에 저장됩니다.


2

내부 문자열은 중복 문자열을 피합니다. Interning은 중복 문자열을 감지하고 교체하는 데 더 많은 CPU 시간을 소비하여 RAM을 절약합니다. 얼마나 많은 참조를 가리키는 지에 관계없이 인터 닝 된 각 문자열의 사본은 하나만 있습니다. 문자열은 변경할 수 없으므로 두 개의 다른 메소드가 우연히 동일한 문자열을 사용하는 경우 동일한 문자열의 사본을 공유 할 수 있습니다. 중복 된 문자열을 공유 문자열로 변환하는 프로세스를 interning.String.intern () 이라고 하며 표준 마스터 문자열의 주소를 제공합니다. 내부 문자열을 같지 않고 간단한 == (포인터를 비교)와 비교할 수 있습니다이것은 문자열의 문자를 하나씩 비교합니다. 문자열은 변경할 수 없기 때문에 인턴 프로세스는 "hippopotamus"와 같은 다른 리터럴의 하위 문자열로 존재할 때 "pot"에 대해 별도의 String 리터럴을 만들지 않는 등 공간을 추가로 절약 할 수 있습니다.

더 많은 http://mindprod.com/jgloss/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");
        }

산출

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");

두 개의 문자열이 독립적으로 생성되면 intern()이를 비교할 수 있으며 참조가 이전에 존재하지 않은 경우 문자열 풀에서 참조를 생성하는 데 도움이됩니다.

을 사용 String s = new String(hi)하면 java는 문자열의 새 인스턴스를 작성하지만을 사용 String s = "hi"하면 java는 코드에 "hi"라는 단어의 인스턴스가 있는지 여부를 확인하고 존재하는 경우 참조를 리턴합니다.

문자열 비교는 참조를 기반으로하기 때문에 참조 intern()를 만드는 데 도움이되고 문자열의 내용을 비교할 수 있습니다.

당신이 사용하는 경우 intern()코드에서, 같은 객체를 참조 캐릭터가 사용하는 공간을 지우고 단지 메모리에 이미 존재하는 동일한 개체의 참조를 반환합니다.

그러나 p5의 경우 다음을 사용합니다.

String p5 = new String(p3);

p3의 내용 만 복사되고 p5가 새로 작성됩니다. 그래서 그것은 억류 되지 않습니다 .

따라서 출력은 다음과 같습니다.

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

string intern () 메소드는 문자열 상수 풀에서 힙 문자열 오브젝트의 정확한 사본을 작성하는 데 사용됩니다. 문자열 상수 풀의 문자열 개체는 자동으로 삽입되지만 힙의 문자열 개체는 그렇지 않습니다. 인턴 만들기의 주요 용도는 메모리 공간을 절약하고 문자열 객체를보다 빠르게 비교하는 것입니다.

출처 : 자바에서 문자열 인턴이란 무엇입니까?


1

말했듯이, 해당 문자열 intern()메서드는 먼저 문자열 풀에서 찾은 다음 찾은 경우 해당 개체를 반환하거나 풀에 새 문자열을 추가합니다.

    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

s1s2문자열 풀 "안녕하세요"를 가리킨 사용하여 두 객체 "Hello".intern()것을 발견 할 것이다 s1s2. 따라서 "s1 == s3"뿐만 아니라 true를 반환합니다 s3.intern().


이것은 실제로 많은 새로운 정보를 제공하지는 않습니다. 이미 예외 답변이 있습니다.
Alexander

0

해당 문자열 상수 풀 객체 참조 를 얻으려면 힙 객체 참조를 사용하여 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

그림보기 여기에 이미지 설명을 입력하십시오

1 단계 : 데이터가 'Rakesh'인 오브젝트는 힙 및 문자열 상수 풀에서 작성됩니다. 또한 s1은 항상 힙 객체를 가리 킵니다.

2 단계 : 힙 객체 참조 s1을 사용하여 intern ()을 사용하여 해당 문자열 상수 풀 객체 참조 s2를 가져 오려고합니다.

3 단계 : 이름이 s3으로 참조되는 문자열 상수 풀에 데이터 'Rakesh'가있는 오브젝트를 의도적으로 작성

"=="연산자는 참조 비교를 의미합니다.

s1 == s2에 대한 거짓 얻기

점점 진정한 S2 == S3에 대한

이 도움을 바랍니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.