Domanda

String s = "";
for(i=0;i<....){
    s = some Assignment;
}

o

for(i=0;i<..){
    String s = some Assignment;
}

Non ho più bisogno di usare 's' fuori dal ciclo. La prima opzione è forse migliore poiché una nuova stringa non viene inizializzata ogni volta. Il secondo comporterebbe tuttavia che l'ambito della variabile si limitasse al ciclo stesso.

EDIT: in risposta alla risposta di Milhous. Sarebbe inutile assegnare la stringa a una costante all'interno di un ciclo, no? No, qui "alcune assegnazioni" indica un valore che cambia dall'elenco che viene ripetuto.

Inoltre, la domanda non è perché sono preoccupato per la gestione della memoria. Voglio solo sapere quale è meglio.

È stato utile?

Soluzione

L'ambito limitato è il migliore

Usa la tua seconda opzione:

for ( ... ) {
  String s = ...;
}

L'ambito non influisce sulle prestazioni

Se disassembli il codice compilato da ciascuno (con lo strumento javap di JDK), vedrai che il ciclo si compila esattamente nelle stesse istruzioni JVM in entrambi i casi. Si noti inoltre che Brian R. Bondy " Opzione # 3 " è identico all'opzione n. 1. Nulla di extra viene aggiunto o rimosso dallo stack quando si utilizza l'ambito più stretto e gli stessi dati vengono utilizzati nello stack in entrambi i casi.

Evitare l'inizializzazione prematura

L'unica differenza tra i due casi è che, nel primo esempio, la variabile s viene inizializzata inutilmente. Questo è un problema separato dall'ubicazione della dichiarazione delle variabili. Questo aggiunge due istruzioni sprecate (per caricare una costante di stringa e memorizzarla in uno slot di stack frame). Un buon strumento di analisi statica ti avvertirà che non stai mai leggendo il valore che assegni a s , e un buon compilatore JIT probabilmente lo eviterà in fase di esecuzione.

Potresti risolverlo semplicemente usando una dichiarazione vuota (cioè String s; ), ma questa è considerata una cattiva pratica e ha un altro effetto collaterale discusso di seguito.

Spesso un valore falso come null viene assegnato a una variabile semplicemente per mettere a tacere un errore del compilatore che una variabile viene letta senza essere inizializzata. Questo errore può essere considerato come un suggerimento che l'ambito della variabile è troppo grande e che viene dichiarato prima che sia necessario per ricevere un valore valido. Dichiarazioni vuote ti costringono a considerare ogni percorso di codice; non ignorare questo prezioso avvertimento assegnando un valore falso.

Conserva slot stack

Come accennato, mentre le istruzioni JVM sono le stesse in entrambi i casi, esiste un sottile effetto collaterale che rende migliore, a livello di JVM, l'uso dell'ambito più limitato possibile. Ciò è visibile nella "tabella delle variabili locali" per il metodo. Considera cosa succede se hai più loop, con le variabili dichiarate in un ambito inutilmente ampio:

void x(String[] strings, Integer[] integers) {
  String s;
  for (int i = 0; i < strings.length; ++i) {
    s = strings[0];
    ...
  }
  Integer n;
  for (int i = 0; i < integers.length; ++i) {
    n = integers[i];
    ...
  }
}

Le variabili s e n potrebbero essere dichiarate all'interno dei rispettivi loop, ma poiché non lo sono, il compilatore utilizza due "slot". nel frame dello stack. Se sono stati dichiarati all'interno del loop, il compilatore può riutilizzare lo stesso slot, riducendo il frame dello stack.

Ciò che conta davvero

Tuttavia, la maggior parte di questi problemi è irrilevante. Un buon compilatore JIT vedrà che non è possibile leggere il valore iniziale che si sta assegnando inutilmente e ottimizzare l'assegnazione. Il salvataggio di uno slot qui o là non creerà o distruggerà la tua applicazione.

L'importante è rendere il tuo codice leggibile e facile da mantenere, e sotto questo aspetto è chiaramente meglio usare un ambito limitato. Più piccolo è il campo di applicazione di una variabile, più facile è capire come viene utilizzata e quale impatto avranno eventuali modifiche al codice.

Altri suggerimenti

Nella teoria , è uno spreco di risorse dichiarare la stringa all'interno del loop. Nella pratica , tuttavia, entrambi i frammenti che hai presentato verranno compilati con lo stesso codice (dichiarazione al di fuori del ciclo).

Quindi, se il tuo compilatore esegue una quantità di ottimizzazione, non c'è alcuna differenza.

In generale sceglierei la seconda, perché l'ambito della variabile 's' è limitato al loop. Vantaggi:

  • Questo è meglio per il programmatore perché non devi preoccuparti che 's' venga riutilizzato da qualche parte più avanti nella funzione
  • Questo è meglio per il compilatore perché l'ambito della variabile è più piccolo e quindi può potenzialmente fare più analisi e ottimizzazione
  • Questo è meglio per i futuri lettori perché non si chiederanno perché la variabile 's' sia dichiarata al di fuori del ciclo se non verrà mai usata in seguito

Se vuoi accelerare per i loop, preferisco dichiarare una variabile massima accanto al contatore in modo che non siano necessarie ricerche ripetute per il condidtion:

anziché

for (int i = 0; i < array.length; i++) {
  Object next = array[i];
}

Preferisco

for (int i = 0, max = array.lenth; i < max; i++) {
  Object next = array[i];
}

Tutte le altre cose che dovrebbero essere considerate sono già state menzionate, quindi solo i miei due centesimi (vedi post di Ericksons)

Greetz, GHad

Per aggiungere un po 'a @ Risposta di Esteban Araya , entrambi richiederanno la creazione di una nuova stringa ogni volta attraverso il ciclo (come valore di ritorno dell'espressione some Assignment ). Queste stringhe devono essere raccolte insieme in entrambi i modi.

So che questa è una vecchia domanda, ma ho pensato di aggiungere un po 'che è leggermente correlato.

Durante la navigazione nel codice sorgente Java ho notato che alcuni metodi, come String.contentEquals (duplicato di seguito) rendono variabili locali ridondanti che sono semplicemente copie di variabili di classe. Credo che ci sia stato un commento da qualche parte, che implicava che l'accesso alle variabili locali è più veloce dell'accesso alle variabili di classe.

In questo caso " v1 " e "v2" sono apparentemente inutili e potrebbero essere eliminati per semplificare il codice, ma sono stati aggiunti per migliorare le prestazioni.

public boolean contentEquals(StringBuffer sb) {
    synchronized(sb) {
        if (count != sb.length())
            return false;
        char v1[] = value;
        char v2[] = sb.getValue();
        int i = offset;
        int j = 0;
        int n = count;
        while (n-- != 0) {
            if (v1[i++] != v2[j++])
                return false;
        }
    }
    return true;
}

Mi sembra che abbiamo bisogno di maggiori specifiche del problema.

Il

s = some Assignment;

non è specificato su che tipo di incarico si tratta. Se il compito è

s = "" + i + "";

quindi è necessario allocare una nuova puntura.

ma se lo è

s = some Constant;

s indicherà semplicemente la posizione della memoria delle costanti, e quindi la prima versione sarebbe più efficiente in termini di memoria.

Sembra un po 'sciocco preoccuparsi di molta ottimizzazione di un ciclo for per un linguaggio interpretato IMHO.

Quando sto usando più thread (50+), ho scoperto che questo è un modo molto efficace per gestire i problemi di thread fantasma con la possibilità di non chiudere correttamente un processo .... se sbaglio, per favore lascia io so perché sbaglio:

Process one;
BufferedInputStream two;
try{
one = Runtime.getRuntime().exec(command);
two = new BufferedInputStream(one.getInputStream());
}
}catch(e){
e.printstacktrace
}
finally{
//null to ensure they are erased
one = null;
two = null;
//nudge the gc
System.gc();
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top