Quando devemos usar o método interno de String em literais de String


187

De acordo com a String # intern () , o internmétodo deve retornar a String do pool de String se a String for encontrada no pool de String, caso contrário, um novo objeto de string será adicionado ao pool de String e a referência dessa String será retornada.

Então eu tentei isso:

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

Eu esperava que s1 and s3 are samefosse impresso como s3 está internado e s1 and s2 are samenão será impresso. Mas o resultado é: ambas as linhas são impressas. Portanto, isso significa que, por padrão, as constantes String são internadas. Mas, se é assim, por que precisamos do internmétodo? Em outras palavras, quando devemos usar esse método?


14
O Javadoc que você vinculou também declara "Todas as strings literais e expressões constantes com valor de string são internadas".
Jorn


1
não é uma duplicata exata ..
Bozho 06/12/2009

1
@ Jorn: isso mesmo. Então, por que temos interncomo método público. Não deveríamos ter interncomo método privado, para que ninguém pudesse ter acesso a ele. Ou existe algum objetivo desse método?
Rakesh Juyal 6/12/2009

2
@RakeshJuyal: o método interno é definido em um tipo de sequência que pode ser literal ou variável de sequência. Como você internaria uma variável se o método fosse privado?
precisa saber é o seguinte

Respostas:


230

Java internamente automaticamente literais de String. Isso significa que, em muitos casos, o operador == parece funcionar para Strings da mesma maneira que para ints ou outros valores primitivos.

Como a internação é automática para literais de String, o intern()método deve ser usado em Strings construídas comnew String()

Usando seu exemplo:

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

retornará:

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

Em todos os casos, além da s4variável, cujo valor foi criado explicitamente usando o newoperador e onde o internmétodo não foi usado no resultado, é uma única instância imutável que está sendo retornada pelo conjunto constante de cadeias da JVM .

Consulte JavaTechniques "String Equality and Interning" para obter mais informações.


Estou assumindo que o Java internamente automaticamente literais String para fins de otimização. Ele pode fazer isso com segurança apenas porque Strings são imutáveis, correto?
styfle

Novo em Java (eu sou do mundo C # .NET) e às vezes vejo em um projeto legado "" .intern () do Java, por isso, se eu entendi corretamente, isso é "absurdo" também para cadeias vazias.
Hfrmobile 11/04

4
@ Miguel explicação agradável, minha pergunta é como pode objeto criado aqui no seu exemplo. Aqui está minha suposição: String s1 = "Rakesh"; primeiro OB1 String s4 = new String("Rakesh");Segundo OB2 Então, o resto de (s2, s3, s5) faz referência ao mesmo objeto (OB1) criado em 'string Pool'. Posso dizer que o .intern()método usado para impedir a criação de um novo objeto se a mesma string disponível em string poolIf minha suposição está errada, então me dê uma direção.
HybrisHelp

1
O link JavaTechniques está quebrado
SJuan76


20

Em um projeto recente, algumas estruturas de dados enormes foram configuradas com dados lidos em um banco de dados (e, portanto, não constantes / literais de String), mas com uma enorme quantidade de duplicação. Era um aplicativo bancário, e coisas como os nomes de um conjunto modesto (talvez 100 ou 200) corporações apareciam em todo lugar. As estruturas de dados já eram grandes e, se todos esses nomes de corp tivessem sido objetos únicos, eles teriam excedido a memória. Em vez disso, todas as estruturas de dados tinham referências aos mesmos objetos de 100 ou 200 String, economizando muito espaço.

Outra pequena vantagem das Strings internas é que ==elas podem ser usadas (com sucesso!) Para comparar Strings, se todas as strings envolvidas tiverem a garantia de serem internadas. Além da sintaxe mais enxuta, isso também é um aprimoramento de desempenho. Mas, como outros já apontaram, isso acarreta um grande risco de introdução de erros de programação; portanto, isso deve ser feito apenas como uma medida desesperada de último recurso.

A desvantagem é que a internação de uma String leva mais tempo do que simplesmente jogá-la na pilha e que o espaço para as Strings internas pode ser limitado, dependendo da implementação do Java. É melhor fazer isso quando você lida com um número razoável conhecido de Strings com muitas duplicações.


@ 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 limitedmesmo que você não use o método interno para constante String, ele será internado automaticamente.
Rakesh Juyal 6/12/2009

2
@Rakesh: Não há muitas constantes String em uma determinada classe normalmente, por isso não é um problema de espaço / tempo com constantes.
David Rodríguez - dribeas 6/12/09

Sim, o comentário de Rakesh não se aplica porque o uso de Strings internas é apenas (explicitamente) feito com Strings que são "geradas" de alguma forma, seja por manipulação interna ou por recuperação de um banco de dados ou algo assim. Com constantes, não temos escolha.
Carl Smotricz

2
+1. Eu acho que este é um bom exemplo de quando internar faz sentido. Eu não concordo com as ==cordas embora.
Alexander Pogrebnyak

1
A partir do Java 7, o "Conjunto de cadeias de caracteres" é implementado no espaço de heap, portanto, obtém todas as vantagens de armazenar estagiários, coleta de lixo e o tamanho não é limitado, podendo ser aumentado para o tamanho de heap. (Você nunca precisará de muito memória para strings)
Anil Uttani

15

Quero adicionar meus 2 centavos no uso ==com seqüências de caracteres internas.

A primeira coisa que String.equalsfaz é this==object.

Portanto, embora haja algum ganho minúsculo de desempenho (você não está chamando um método), do ponto de vista do mantenedor, o uso ==é um pesadelo, porque algumas seqüências de caracteres internas tendem a se tornar não-internas.

Portanto, sugiro não confiar no caso especial de ==para seqüências de caracteres internas, mas sempre use equalscomo Gosling pretendia.

EDIT: internado tornando-se não internado:

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

Na versão 2.0, o mantenedor decidiu tornar hasReferenceValpúblico, sem entrar em muitos detalhes, que espera uma matriz de seqüências internas.

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

Agora você tem um bug, que pode ser muito difícil de encontrar, porque na maioria dos casos a matriz contém valores literais e, às vezes, uma string não literal é usada. Se equalsfosse usado em vez de ==, hasReferenceValcontinuaria funcionando. Mais uma vez, o ganho de desempenho é minúsculo, mas o custo de manutenção é alto.


"algumas seqüências de caracteres internadas tendem a se tornar não internadas." uau, isso seria ... estranho. Você pode citar uma referência, por favor?
Carl Smotricz 06/12/2009

2
OK, pensei que você estava se referindo a Strings realmente saindo da piscina interna e entrando na pilha graças à magia na JVM. O que você está dizendo é que == aumenta a probabilidade de certas classes de erros do programador.
Carl Smotricz 06/12/2009

"Então, sugiro não confiar no caso especial de == para seqüências de caracteres internas, mas sempre use iguais como Gosling pretendia." Você tem uma citação ou comentário direto de Gosling afirmando isso? Se for esse o caso, por que ele se incomodou em colocar intern () e o uso de == no idioma?

1
intern não é bom para comparação direta (==), mesmo que funcione se as duas cadeias de caracteres forem internadas. é ótimo diminuir a memória total usada: quando a mesma string é usada em mais de um local.
tgkprog

12

Literais e constantes de string são internados por padrão. Ou seja, "foo" == "foo"(declarado pelos literais String), mas new String("foo") != new String("foo").


4
Assim, a pergunta é quando devemos usar intern,
Rakesh Juyal

isso foi apontado para stackoverflow.com/questions/1833581/when-to-use-intern e várias outras perguntas, algumas delas de ontem.
Bozho 6/12/09

Deixe-me saber se meu entendimento para esta afirmação String literals and constants are interned by default:, está correto. new String("foo")-> Aqui, um literal "foo" de String é criado no pool de String e um no heap, para que sejam criados 2 objetos no total.
dkb

8

Aprenda Java String Intern - uma vez por todas

Strings em java são objetos imutáveis ​​por design. Portanto, dois objetos de sequência, mesmo com o mesmo valor, serão objetos diferentes por padrão. No entanto, se desejarmos economizar memória, poderemos indicar o uso da mesma memória por um conceito chamado string intern.

As regras abaixo ajudarão você a entender o conceito em termos claros:

  1. A classe String mantém um pool interno que está inicialmente vazio. Esse pool deve garantir a conter objetos de sequência com apenas valores exclusivos.
  2. Todos os literais de cadeia de caracteres com o mesmo valor devem ser considerados o mesmo objeto de local de memória porque, de outra forma, não têm noção de distinção. Portanto, todos esses literais com o mesmo valor criarão uma única entrada no pool interno e se referirão ao mesmo local da memória.
  3. A concatenação de dois ou mais literais também é literal. (Portanto, a regra nº 2 será aplicável a eles)
  4. Cada string criada como objeto (ou seja, por qualquer outro método, exceto como literal) terá diferentes locais de memória e não fará nenhuma entrada no pool interno
  5. A concatenação de literais com não literais fará com que não seja literal. Assim, o objeto resultante terá um novo local de memória e NÃO fará uma entrada no pool interno.
  6. A chamada do método interno em um objeto de cadeia de caracteres cria um novo objeto que entra no conjunto interno ou retorna um objeto existente do conjunto que tem o mesmo valor. A invocação em qualquer objeto que não esteja no pool interno NÃO move o objeto para o pool. Em vez disso, cria outro objeto que entra no pool.

Exemplo:

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

Nota: Os casos motivacionais para estagiário de string não são discutidos aqui. No entanto, a economia de memória será definitivamente um dos objetivos principais.


Obrigado por # 3, eu não sei :)
Kaay

4

você deve distinguir dois períodos de tempo, que são tempo de compilação e tempo de execução. por exemplo:

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

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

por um lado, no exemplo 1, descobrimos que todos os resultados são retornados verdadeiros, pois, no tempo de compilação, a jvm colocará o "teste" no conjunto de cadeias literais, se a jvm find "test" existir, então ele usará o existente, no exemplo 1, as seqüências de "teste" são todas apontadas para o mesmo endereço de memória, portanto o exemplo 1 retornará verdadeiro. por outro lado, no exemplo 2, o método de substring () é executado no tempo de execução, no caso de "test" == "! test" .substring (1), o pool criará dois objetos de string " test "e"! test ", portanto, são objetos de referência diferentes; portanto, este caso retornará false, no caso de" test "=="! test ".substring (1) .intern (), o método intern ( ) colocará ""! test ".substring (1)" no pool de cadeias literais,


3

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

o interning de string é um método de armazenar apenas uma cópia de cada valor distinto de string, que deve ser imutável. As cadeias internas tornam algumas tarefas de processamento de cadeias mais eficientes em termos de tempo ou espaço, exigindo mais tempo quando a cadeia é criada ou internada. Os valores distintos são armazenados em um conjunto interno de cadeias.


2

Strings Internas evitam Strings duplicadas. Internar economiza RAM às custas de mais tempo de CPU para detectar e substituir seqüências de caracteres duplicadas. Há apenas uma cópia de cada String que foi internada, não importa quantas referências apontem para ela. Como Strings são imutáveis, se dois métodos diferentes usam acidentalmente a mesma String, eles podem compartilhar uma cópia da mesma String. O processo de conversão de Strings duplicadas em compartilhadas é chamado interning.String.intern () fornece o endereço da String canônica principal. Você pode comparar Strings internadas com simple == (que compara ponteiros) em vez de iguaisque compara os caracteres da String, um por um. Como Strings são imutáveis, o processo interno é livre para economizar espaço, por exemplo, não criando um literal String separado para "pot" quando ele existe como uma substring de algum outro literal, como "hippopotamus".

Para ver mais 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");
        }

RESULTADO

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

Quando duas strings são criadas independentemente, intern()permite compará-las e também ajuda na criação de uma referência no pool de strings, se a referência não existia antes.

Quando você usa String s = new String(hi), o java cria uma nova instância da string, mas quando você usa String s = "hi", o java verifica se há uma instância da palavra "oi" no código ou não e, se existir, apenas retorna a referência.

Como a comparação de strings é baseada em referência, intern()ajuda na criação de uma referência e permite comparar o conteúdo das strings.

Quando você usa intern()o código, ele limpa o espaço usado pela string referente ao mesmo objeto e apenas retorna a referência do mesmo objeto já existente na memória.

Mas no caso de p5 quando você estiver usando:

String p5 = new String(p3);

Somente o conteúdo de p3 é copiado e p5 é criado recentemente. Portanto, não é internado .

Portanto, a saída será:

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

O método string intern () é usado para criar uma cópia exata do objeto string do heap no pool constante de strings. Os objetos de cadeia no conjunto constante de cadeias são internados automaticamente, mas os objetos de cadeia na pilha não são. O principal uso da criação de estagiários é economizar espaço de memória e realizar uma comparação mais rápida de objetos de sequência.

Fonte: O que é string intern em java?


1

Como você disse, esse intern()método de string primeiro encontrará no pool de String, se encontrar, retornará o objeto que aponta para ele ou adicionará uma nova String ao pool.

    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

O s1e s2dois objetos que apontam para a piscina String "Olá", e usando "Hello".intern()vai achar que s1e s2. Então "s1 == s3"retorna true, bem como para o s3.intern().


Isso realmente não fornece muita informação nova. Já existe uma resposta exceto.
Alexander

0

Usando a referência de objeto de heap, se queremos obter a referência de objeto de pool constante de string correspondente , devemos procurar 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

Vista pictórica insira a descrição da imagem aqui

Etapa 1: o objeto com dados 'Rakesh' é criado no pool constante de heap e string. Também s1 está sempre apontando para o objeto heap.

Etapa 2: usando a referência de objeto de pilha s1, estamos tentando obter a referência de objeto de pool constante de string correspondente s2, usando intern ()

Etapa 3: criar intencionalmente um objeto com dados 'Rakesh' no pool constante de strings, referenciado pelo nome s3

Como operador "==" destinado à comparação de referência.

Ficando falso para s1 == s2

Obtendo a verdade para s2 == s3

Espero que esta ajuda!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.