Quante stringhe vengono create in memoria durante la concatenazione di stringhe in Java?


17

Mi è stato chiesto delle stringhe immutabili in Java. Mi è stato assegnato il compito di scrivere una funzione che concatenasse un numero di "a" s in una stringa.

Cosa ho scritto:

public String foo(int n) {
    String s = "";
    for (int i = 0; i < n; i++) {
        s = s + "a"
    }
    return s;
}

Mi è stato poi chiesto quante stringhe questo programma avrebbe generato, supponendo che la raccolta dei rifiuti non avvenga. I miei pensieri per n = 3 erano

  1. ""
  2. "un"
  3. "un"
  4. "aa"
  5. "un"
  6. "Aaa"
  7. "un"

Essenzialmente vengono create 2 stringhe in ogni iterazione del ciclo. Tuttavia, la risposta era n 2 . Quali stringhe verranno create in memoria da questa funzione e perché è così?


15
Se ti viene offerto questo lavoro, scappa, corri molto veloce .......
mattnz,

@mattnz per diversi motivi (e non solo a causa del codice scritto).

3
Ciò richiede il runtime O (n ^ 2) a meno che JIT non ottimizzi il ciclo, ma non crei n ^ 2 stringhe.
user2357112 supporta Monica il

Risposte:


26

Mi è stato poi chiesto quante stringhe questo programma avrebbe generato, supponendo che la raccolta dei rifiuti non avvenga. I miei pensieri per n = 3 erano (7)

Le stringhe 1 ( "") e 2 ( "a") sono le costanti nel programma, non sono create come parte delle cose ma vengono "internate" perché sono costanti di cui il compilatore conosce. Maggiori informazioni al riguardo sono disponibili su String interning su Wikipedia.

Ciò rimuove anche le stringhe 5 e 7 dal conteggio poiché sono le stesse della "a"stringa n. 2. Questo lascia le stringhe # 3, # 4 e # 6. La risposta è "3 stringhe vengono create per n = 3" usando il tuo codice.

Il conteggio di n 2 è ovviamente errato perché in n = 3, questo sarebbe 9 e anche dalla risposta nel caso peggiore, che era solo 7. Se le stringhe non internate fossero corrette, la risposta avrebbe dovuto essere 2n + 1.

Quindi, la domanda su come dovresti farlo?

Poiché la stringa è immutabile , vuoi qualcosa di mutabile, qualcosa che puoi cambiare senza creare nuovi oggetti. Questo è StringBuilder .

La prima cosa da guardare sono i costruttori. In questo caso sappiamo quanto tempo sarà la stringa e c'è un costruttore StringBuilder(int capacity) che significa che allociamo esattamente tutto ciò di cui abbiamo bisogno.

Successivamente, "a"non deve essere una stringa , ma può piuttosto essere un personaggio 'a'. Questo ha un piccolo miglioramento delle prestazioni quando si chiama append(String)vs append(char)- con il append(String), il metodo deve scoprire quanto è lunga la String e fare un po 'di lavoro su questo. D'altra parte, charè sempre esattamente lungo un personaggio.

Le differenze di codice sono visibili in StringBuilder.append (String) vs StringBuilder.append (char) . Non è qualcosa di cui preoccuparsi troppo , ma se stai cercando di impressionare il datore di lavoro è meglio usare le migliori pratiche possibili.

Quindi, come appare quando lo metti insieme?

public String foo(int n) {
    StringBuilder sb = new StringBuilder(n);
    for (int i = 0; i < n; i++) {
        sb.append('a');
    }
    return sb.toString();
}

Sono stati creati uno StringBuilder e uno String. Non è necessario internare stringhe extra.


Scrivi alcuni altri semplici programmi in Eclipse. Installa pmd ed eseguilo sul codice che scrivi. Nota di cosa si lamenta e risolvi queste cose. Avrebbe trovato la modifica di una stringa con + in un ciclo e se l'avessi modificata in StringBuilder, avrebbe forse trovato la capacità iniziale, ma avrebbe sicuramente colto la differenza tra .append("a")e.append('a')


9

Ad ogni iterazione, un nuovo Stringviene creato +dall'operatore e assegnato a s. Dopo il ritorno, tutti tranne l'ultimo vengono raccolti.

Le costanti di stringa gradiscono ""e "a"non vengono create ogni volta, si tratta di stringhe internate . Poiché le stringhe sono immutabili, possono essere liberamente condivise; questo succede alle costanti di stringa.

Per concatenare in modo efficiente le stringhe, utilizzare StringBuilder.


Le persone durante l'intervista hanno effettivamente discusso se il letterale fosse o meno, e hanno deciso che i letterali venivano creati ogni volta. Ma questo ha più senso.
ahalbert,

6
Come si "discute" di ciò che fa una lingua, sicuramente si legge la specifica e si sa con certezza, o la sua non è definita e quindi, non esiste una risposta corretta .....
mattnz

@mattnz Potrebbe essere interessante sapere cosa fa il compilatore / runtime che stai usando, anche quando si tratta di dettagli di implementazione. Questo vale soprattutto per le prestazioni.
svick,

1
@svick: puoi guadagnare molto facendo ipotesi, quindi il compilatore viene aggiornato, un'ottimizzazione modificata, ecc. Il comportamento cambia causando bug perché ti sei affidato a comportamenti non specificati piuttosto che a comportamenti definiti. Sai cosa dicono sull'ottimizzazione: a) lascialo agli esperti eb) non sei ancora un esperto. :) Se la dipendenza è basata solo sulle prestazioni, ma sempre sulle specifiche del linguaggio, perdi solo le prestazioni. Molte volte ho visto il codice che si basava su comportamenti non specificati o specifici del compilatore rompersi in modi inaspettati (principalmente C e C ++).
mattnz,

@mattnz Quindi come proponi di prendere decisioni relative alle prestazioni? Di solito, il meglio che puoi ottenere dalle specifiche / dalla documentazione sono le complessità di grandi dimensioni, ma non è abbastanza. In ogni caso, le prestazioni dipenderanno sempre dall'implementazione, quindi penso che sia giusto fare affidamento sui dettagli dell'implementazione quando si tratta di prestazioni.
svick,

4

Come spiega MichaelT nella sua risposta, il codice alloca stringhe O (n). Ma alloca anche O (n 2 ) byte di memoria e viene eseguito nel tempo O (n 2 ).

Alloca O (n 2 ) byte, perché le stringhe che stai allocando hanno lunghezze 0, 1, 2,…, n-1, n, che si somma a (n 2 + n) / 2 = O (n 2 ).

Il tempo è anche O (n 2 ), perché l'allocazione della stringa i-esima richiede la copia della stringa (i-1), che ha lunghezza i-1. Ciò significa che ogni byte allocato deve essere copiato, il che richiederà tempo O (n 2 ).

Forse è questo che intendevano gli intervistatori?


L'equazione non dovrebbe essere (n ^ 2 + n) / 2, come qui ?
HeyJude,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.