Variablen innerhalb oder außerhalb einer Schleife deklarieren

236

Warum funktioniert das Folgende gut?

String str;
while (condition) {
    str = calculateStr();
    .....
}

Aber dieser soll gefährlich / falsch sein:

while (condition) {
    String str = calculateStr();
    .....
}

Ist es notwendig, Variablen außerhalb der Schleife zu deklarieren?

Harry Joy
quelle

Antworten:

289

Der Umfang lokaler Variablen sollte immer so klein wie möglich sein.

In Ihrem Beispiel nehme ich an strist nicht außerhalb der verwendeten whileSchleife, sonst würden Sie nicht die Frage stellen, weil es in der Erklärung derwhile der Schleife keine Option sein würde, da es nicht kompilieren würde.

Also, da strist nicht außerhalb der Schleife verwendet, der kleinste mögliche Spielraum für strist innerhalb der while - Schleife.

Die Antwort lautet also nachdrücklich, dass strunbedingt innerhalb der while-Schleife deklariert werden sollte. Kein Wenn, kein Und, kein Aber.

Der einzige Fall, in dem diese Regel verletzt werden könnte, ist, wenn es aus irgendeinem Grund von entscheidender Bedeutung ist, dass jeder Taktzyklus aus dem Code herausgedrückt wird. In diesem Fall möchten Sie möglicherweise in Betracht ziehen, etwas in einem äußeren Bereich zu instanziieren und stattdessen wiederzuverwenden bei jeder Iteration eines inneren Bereichs erneut instanziieren. Dies gilt jedoch aufgrund der Unveränderlichkeit von Strings in Java nicht für Ihr Beispiel: Eine neue Instanz von str wird immer am Anfang Ihrer Schleife erstellt und muss am Ende weggeworfen werden gibt es dort keine optimierungsmöglichkeit.

EDIT: (fügt meinen Kommentar unten in die Antwort ein)

In jedem Fall besteht der richtige Weg darin, den gesamten Code richtig zu schreiben, eine Leistungsanforderung für Ihr Produkt festzulegen, Ihr Endprodukt an dieser Anforderung zu messen und die Dinge zu optimieren, wenn sie diese nicht erfüllen. Und was normalerweise passiert, ist, dass Sie an nur wenigen Stellen Möglichkeiten finden, einige nette und formale algorithmische Optimierungen bereitzustellen, die unser Programm dazu bringen, seine Leistungsanforderungen zu erfüllen, anstatt Ihre gesamte Codebasis zu durchlaufen und Dinge zu optimieren und zu hacken um hier und da Taktzyklen zu quetschen.

Mike Nakis
quelle
2
Abfrage zum letzten Absatz: Wenn es sich um einen anderen String handelt, der nicht unveränderlich ist, wirkt er sich dann aus?
Harry Joy
1
@HarryJoy Ja, natürlich nehmen Sie zum Beispiel StringBuilder, der veränderlich ist. Wenn Sie einen StringBuilder verwenden, um in jeder Iteration der Schleife einen neuen String zu erstellen, können Sie die Dinge optimieren, indem Sie den StringBuilder außerhalb der Schleife zuweisen. Dies ist jedoch keine empfehlenswerte Praxis. Wenn Sie dies ohne guten Grund tun, handelt es sich um eine vorzeitige Optimierung.
Mike Nakis
7
@HarryJoy Der richtige Weg, um Dinge zu tun, besteht darin, Ihren gesamten Code richtig zu schreiben , eine Leistungsanforderung für Ihr Produkt festzulegen, Ihr Endprodukt an dieser Anforderung zu messen und, wenn es diese nicht erfüllt, die Dinge zu optimieren. Und weisst du was? Normalerweise können Sie an nur wenigen Stellen einige nette und formale algorithmische Optimierungen vornehmen, die den Trick ausführen, anstatt Ihre gesamte Codebasis zu durchsuchen und Dinge zu optimieren und zu hacken, um hier und da Taktzyklen zu komprimieren.
Mike Nakis
2
@ MikeNakis ich denke du denkst in sehr engem Rahmen.
Siten
5
Sie sehen, moderne Multi-Gigahertz-, Multi-Core-, Pipeline- und Multi-Level-Memory-Cache-CPUs ermöglichen es uns, uns auf das Befolgen von Best Practices zu konzentrieren, ohne uns um Taktzyklen kümmern zu müssen. Darüber hinaus ist eine Optimierung nur dann ratsam, wenn festgestellt wurde, dass dies erforderlich ist, und wenn dies erforderlich ist, erzielen einige stark lokalisierte Optimierungen normalerweise die gewünschte Leistung, sodass nicht der gesamte Code verschmutzt werden muss mit kleinen Hacks im Namen der Leistung.
Mike Nakis
293

Ich habe den Bytecode dieser beiden (ähnlichen) Beispiele verglichen:

Schauen wir uns 1. Beispiel an :

package inside;

public class Test {
    public static void main(String[] args) {
        while(true){
            String str = String.valueOf(System.currentTimeMillis());
            System.out.println(str);
        }
    }
}

nach javac Test.java, javap -c Testbringen Sie:

public class inside.Test extends java.lang.Object{
public inside.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   3:   invokestatic    #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
   6:   astore_1
   7:   getstatic       #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   10:  aload_1
   11:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   14:  goto    0

}

Schauen wir uns das 2. Beispiel an :

package outside;

public class Test {
    public static void main(String[] args) {
        String str;
        while(true){
            str =  String.valueOf(System.currentTimeMillis());
            System.out.println(str);
        }
    }
}

nach javac Test.java, javap -c Testbringen Sie:

public class outside.Test extends java.lang.Object{
public outside.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   3:   invokestatic    #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
   6:   astore_1
   7:   getstatic       #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   10:  aload_1
   11:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   14:  goto    0

}

Die Beobachtungen zeigen, dass es keinen Unterschied zwischen diesen beiden Beispielen gibt. Es ist das Ergebnis von JVM-Spezifikationen ...

Im Namen der besten Codierungspraxis wird jedoch empfohlen, die Variable im kleinstmöglichen Bereich zu deklarieren (in diesem Beispiel befindet sie sich innerhalb der Schleife, da dies der einzige Ort ist, an dem die Variable verwendet wird).

PrimosK
quelle
3
Es ist das Ergebnis der JVM-Soecification, nicht der Compiler-Optimierung. Die für eine Methode erforderlichen Stapelschlitze werden alle beim Eintritt in die Methode zugewiesen. So wird der Bytecode angegeben.
Marquis von Lorne
2
@Arhimed gibt es noch einen weiteren Grund, es in die Schleife (oder nur in den Block '{}') zu setzen: Der Compiler verwendet den im Stapelrahmen zugewiesenen Speicher für die Variable in einem anderen Bereich wieder, wenn Sie in diesem anderen Bereich eine Übervariable deklarieren .
Serge
1
Wenn es eine Liste von Datenobjekten durchläuft, macht es dann einen Unterschied für einen Großteil der Daten? Wahrscheinlich 40 Tausend.
Mithun Khatri
7
Für jede von Ihnen finalLiebhabern: erklären , strwie finalin dem insidePaket Fall auch keinen Unterschied macht =)
skia.heliou
27

Das Deklarieren von Objekten im kleinsten Bereich verbessert die Lesbarkeit .

Die Leistung spielt für die heutigen Compiler keine Rolle. (In diesem Szenario)
Aus Wartungssicht ist die zweite Option besser.
Deklarieren und initialisieren Sie Variablen an derselben Stelle im engstmöglichen Rahmen.

Wie Donald Ervin Knuth sagte:

"Wir sollten kleine Wirkungsgrade vergessen, etwa in 97% der Fälle: Vorzeitige Optimierung ist die Wurzel allen Übels."

dh) Situation, in der ein Programmierer Leistungsüberlegungen das Design eines Codeteils beeinflussen lässt. Dies kann in einem Entwurf zur Folge hat, dass ist nicht so sauber , wie es hätte sein können oder Code, der falsch ist, da der Code ist kompliziert durch die Optimierung und der Programmierer durch abgelenkt zu optimieren .

Chandra Sekhar
quelle
1
"2. Option hat etwas schnellere Leistung" => haben Sie es gemessen? Einer der Antworten zufolge ist der Bytecode der gleiche, sodass ich nicht sehe, wie unterschiedlich die Leistung sein kann.
Assylias
Es tut mir leid, aber das ist wirklich nicht der richtige Weg, um die Leistung eines Java-Programms zu testen (und wie kann man die Leistung einer Endlosschleife überhaupt testen?)
Assylias
Ich stimme Ihren anderen Punkten zu - ich glaube nur, dass es keinen Leistungsunterschied gibt.
Assylias
11

wenn Sie auch straußerhalb der Schleife verwenden möchten; erkläre es draußen. Ansonsten ist die 2. Version in Ordnung.

Azodious
quelle
11

Bitte springen Sie zur aktualisierten Antwort ...

Für diejenigen, die Wert auf Leistung legen, nehmen Sie das System.out heraus und begrenzen Sie die Schleife auf 1 Byte. Unter Verwendung von double (Test 1/2) und String (3/4) werden die verstrichenen Zeiten in Millisekunden mit Windows 7 Professional 64 Bit und JDK-1.7.0_21 angegeben. Bytecodes (auch unten für Test1 und Test2 angegeben) sind nicht identisch. Ich war zu faul, um mit veränderlichen und relativ komplexen Objekten zu testen.

doppelt

Test1 dauerte: 2710 ms

Test2 dauerte: 2790 ms

String (in den Tests einfach double durch string ersetzen)

Test3 dauerte: 1200 ms

Test4 dauerte: 3000 ms

Bytecode kompilieren und abrufen

javac.exe LocalTest1.java

javap.exe -c LocalTest1 > LocalTest1.bc


public class LocalTest1 {

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        double test;
        for (double i = 0; i < 1000000000; i++) {
            test = i;
        }
        long finish = System.currentTimeMillis();
        System.out.println("Test1 Took: " + (finish - start) + " msecs");
    }

}

public class LocalTest2 {

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        for (double i = 0; i < 1000000000; i++) {
            double test = i;
        }
        long finish = System.currentTimeMillis();
        System.out.println("Test1 Took: " + (finish - start) + " msecs");
    }
}


Compiled from "LocalTest1.java"
public class LocalTest1 {
  public LocalTest1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
       3: lstore_1
       4: dconst_0
       5: dstore        5
       7: dload         5
       9: ldc2_w        #3                  // double 1.0E9d
      12: dcmpg
      13: ifge          28
      16: dload         5
      18: dstore_3
      19: dload         5
      21: dconst_1
      22: dadd
      23: dstore        5
      25: goto          7
      28: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
      31: lstore        5
      33: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      36: new           #6                  // class java/lang/StringBuilder
      39: dup
      40: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
      43: ldc           #8                  // String Test1 Took:
      45: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      48: lload         5
      50: lload_1
      51: lsub
      52: invokevirtual #10                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
      55: ldc           #11                 // String  msecs
      57: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      60: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      63: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      66: return
}


Compiled from "LocalTest2.java"
public class LocalTest2 {
  public LocalTest2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
       3: lstore_1
       4: dconst_0
       5: dstore_3
       6: dload_3
       7: ldc2_w        #3                  // double 1.0E9d
      10: dcmpg
      11: ifge          24
      14: dload_3
      15: dstore        5
      17: dload_3
      18: dconst_1
      19: dadd
      20: dstore_3
      21: goto          6
      24: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
      27: lstore_3
      28: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      31: new           #6                  // class java/lang/StringBuilder
      34: dup
      35: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
      38: ldc           #8                  // String Test1 Took:
      40: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      43: lload_3
      44: lload_1
      45: lsub
      46: invokevirtual #10                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
      49: ldc           #11                 // String  msecs
      51: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      54: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      57: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      60: return
}

AKTUALISIERTE ANTWORT

Es ist wirklich nicht einfach, die Leistung mit allen JVM-Optimierungen zu vergleichen. Es ist jedoch etwas möglich. Besserer Test und detaillierte Ergebnisse in Google Caliper

  1. Einige Details im Blog: Sollten Sie eine Variable innerhalb einer Schleife oder vor der Schleife deklarieren?
  2. GitHub-Repository: https://github.com/gunduru/jvdt
  3. Testergebnisse für Doppelfall und 100M-Schleife (und ja alle JVM-Details): https://microbenchmarks.appspot.com/runs/b1cef8d1-0e2c-4120-be61-a99faff625b4

DeclaredBefore 1.759.209 DeclaredInside 2.242.308

  • Vor 1.759,209 ns deklariert
  • DeclaredInside 2,242.308 ns

Teilprüfcode für doppelte Erklärung

Dies ist nicht identisch mit dem obigen Code. Wenn Sie nur eine Dummy-Schleife codieren, überspringt JVM diese, sodass Sie zumindest etwas zuweisen und zurückgeben müssen. Dies wird auch in der Caliper-Dokumentation empfohlen.

@Param int size; // Set automatically by framework, provided in the Main
/**
* Variable is declared inside the loop.
*
* @param reps
* @return
*/
public double timeDeclaredInside(int reps) {
    /* Dummy variable needed to workaround smart JVM */
    double dummy = 0;

    /* Test loop */
    for (double i = 0; i <= size; i++) {

        /* Declaration and assignment */
        double test = i;

        /* Dummy assignment to fake JVM */
        if(i == size) {
            dummy = test;
        }
    }
    return dummy;
}

/**
* Variable is declared before the loop.
*
* @param reps
* @return
*/
public double timeDeclaredBefore(int reps) {

    /* Dummy variable needed to workaround smart JVM */
    double dummy = 0;

    /* Actual test variable */
    double test = 0;

    /* Test loop */
    for (double i = 0; i <= size; i++) {

        /* Assignment */
        test = i;

        /* Not actually needed here, but we need consistent performance results */
        if(i == size) {
            dummy = test;
        }
    }
    return dummy;
}

Zusammenfassung: declareBefore zeigt eine bessere Leistung an - wirklich winzig - und verstößt gegen das Prinzip des kleinsten Anwendungsbereichs. JVM sollte dies eigentlich für Sie tun

Onur Günduru
quelle
Ungültige Testmethode, und Sie geben keine Erklärung für Ihre Ergebnisse.
Marquis von Lorne
1
@EJP Dies sollte für diejenigen, die sich für das Thema interessieren, ziemlich klar sein. Die Methodik wird der Antwort von PrimosK entnommen, um nützlichere Informationen bereitzustellen. Um ehrlich zu sein, habe ich keine Ahnung, wie ich diese Antwort verbessern kann. Vielleicht können Sie auf Bearbeiten klicken und uns zeigen, wie es richtig geht?
Onur Günduru
2
1) Java Bytecode wird zur Laufzeit optimiert (neu angeordnet, reduziert usw.). Es ist also egal, was in den .class-Dateien zu viel geschrieben wird. 2) Es gibt 1.000.000.000 Läufe, um einen Leistungsgewinn von 2,8 Sekunden zu erzielen, was ungefähr 2,8 ns pro Lauf im Vergleich zu einem sicheren und korrekten Programmierstil entspricht. Ein klarer Gewinner für mich. 3) Da Sie keine Informationen zum Aufwärmen angeben, sind Ihre Timings völlig nutzlos.
Hardcoded
@ Hardcoded bessere Tests / Micro Benchmarking mit Bremssattel nur für Doppel- und 100M-Schleifen. Ergebnisse online, wenn Sie andere Fälle möchten, können Sie diese gerne bearbeiten.
Onur Günduru
Danke, dies beseitigt die Punkte 1) und 3). Aber selbst wenn die Zeit auf ~ 5 ns pro Zyklus gestiegen ist, ist dies immer noch eine Zeit, die ignoriert werden muss. Theoretisch gibt es ein kleines Optimierungspotential. In Wirklichkeit ist das, was Sie pro Zyklus tun, normalerweise viel teurer. Das Potenzial würde also in einigen Minuten oder sogar Stunden maximal einige Sekunden betragen. Es gibt andere Optionen mit höherem Potenzial (z. B. Fork / Join, parallele Streams), die ich prüfen würde, bevor ich Zeit für diese Art von Optimierungen auf niedriger Ebene verbringe.
Hardcoded
7

Eine Lösung für dieses Problem könnte darin bestehen, einen variablen Bereich bereitzustellen, der die while-Schleife einschließt:

{
  // all tmp loop variables here ....
  // ....
  String str;
  while(condition){
      str = calculateStr();
      .....
  }
}

Sie werden automatisch de-referenziert, wenn der äußere Bereich endet.

Morten Madsen
quelle
6

Je weniger Bereich die Variable im Inneren sichtbar ist, desto besser.

Jan Zyka
quelle
5

Wenn Sie die strAfter-the-while-Schleife (bereichsbezogen) nicht verwenden müssen, gilt die zweite Bedingung, d. H.

  while(condition){
        String str = calculateStr();
        .....
    }

ist besser, wenn Sie ein Objekt auf dem Stapel nur definieren, wenn das conditionwahr ist. Ich benutze es, wenn du es brauchst

Cratylus
quelle
2
Beachten Sie, dass selbst in der ersten Variante kein Objekt erstellt wird, wenn die Bedingung falsch ist.
Philipp Wendler
@ Phillip: Ja du hast recht. Mein Fehler. Ich dachte so wie es jetzt ist. Was denkst du?
Cratylus
1
Nun, "Definieren eines Objekts auf dem Stapel" ist ein etwas seltsamer Begriff in der Java-Welt. Außerdem ist das Zuweisen einer Variablen auf dem Stapel zur Laufzeit normalerweise ein Noop. Warum also die Mühe machen? Das Ziel, dem Programmierer zu helfen, ist das eigentliche Problem.
Philipp Wendler
3

Ich denke, die beste Ressource zur Beantwortung Ihrer Frage wäre der folgende Beitrag:

Unterschied zwischen der Deklaration von Variablen vor oder in der Schleife?

Nach meinem Verständnis wäre dieses Ding sprachabhängig. IIRC Java optimiert dies, sodass es keinen Unterschied gibt, aber JavaScript (zum Beispiel) übernimmt jedes Mal die gesamte Speicherzuweisung in der Schleife. Insbesondere in Java würde die zweite nach Abschluss der Profilerstellung schneller ausgeführt.

Naveen Goyal
quelle
3

Wie viele Leute darauf hingewiesen haben,

String str;
while(condition){
    str = calculateStr();
    .....
}

ist NICHT besser als das:

while(condition){
    String str = calculateStr();
    .....
}

Deklarieren Sie also keine Variablen außerhalb ihres Gültigkeitsbereichs, wenn Sie sie nicht wiederverwenden ...

Pavan
quelle
1
außer wahrscheinlich so: link
Dainius Kreivys
2

Wenn Sie String str außerhalb der wile-Schleife deklarieren, kann innerhalb und außerhalb der while-Schleife auf ihn verwiesen werden. Wenn Sie String str innerhalb der while-Schleife deklarieren, kann nur innerhalb der while-Schleife darauf verwiesen werden.

Jay Tomten
quelle
1

Variablen sollten so nahe wie möglich an ihrem Verwendungsort deklariert werden.

Dies erleichtert RAII (Resource Acquisition Is Initialization) .

Es hält den Umfang der Variablen eng. Dadurch kann der Optimierer besser arbeiten.

vikiiii
quelle
1

Laut Google Android Development Guide sollte der variable Umfang begrenzt sein. Bitte überprüfen Sie diesen Link:

Variablenbereich begrenzen

James Jithin
quelle
1

Die strVariable wird verfügbar sein und auch nach der Ausführung unter dem Code etwas Speicherplatz reservieren.

 String str;
    while(condition){
        str = calculateStr();
        .....
    }

Die strVariable ist nicht verfügbar und es wird auch der Speicher freigegeben, der strim folgenden Code für die Variable zugewiesen wurde .

while(condition){
    String str = calculateStr();
    .....
}

Wenn wir dem zweiten folgen, wird dies sicherlich unseren Systemspeicher reduzieren und die Leistung steigern.

Ganesa Vijayakumar
quelle
0

Das Deklarieren innerhalb der Schleife begrenzt den Umfang der jeweiligen Variablen. Es hängt alles von den Anforderungen des Projekts am Umfang der Variablen ab.

ab02
quelle
0

Die oben genannte Frage ist wirklich ein Programmierproblem. Wie möchten Sie Ihren Code programmieren? Wo muss auf die 'STR' zugegriffen werden? Es ist nicht sinnvoll, eine Variable zu deklarieren, die lokal als globale Variable verwendet wird. Grundlagen der Programmierung glaube ich.

Abhishek Bhandari
quelle
-1

Diese beiden Beispiele führen zu demselben Ergebnis. Mit der ersten Option können Sie die strVariable jedoch außerhalb der while-Schleife verwenden. der zweite ist nicht.

Olyanren
quelle
-1

Warnung für fast alle in dieser Frage: Hier ist ein Beispielcode, bei dem es innerhalb der Schleife auf meinem Computer mit Java 7 leicht 200-mal langsamer sein kann (und der Speicherverbrauch ist auch etwas anders). Es geht aber um Allokation und nicht nur um Umfang.

public class Test
{
    private final static int STUFF_SIZE = 512;
    private final static long LOOP = 10000000l;

    private static class Foo
    {
        private long[] bigStuff = new long[STUFF_SIZE];

        public Foo(long value)
        {
            setValue(value);
        }

        public void setValue(long value)
        {
            // Putting value in a random place.
            bigStuff[(int) (value % STUFF_SIZE)] = value;
        }

        public long getValue()
        {
            // Retrieving whatever value.
            return bigStuff[STUFF_SIZE / 2];
        }
    }

    public static long test1()
    {
        long total = 0;

        for (long i = 0; i < LOOP; i++)
        {
            Foo foo = new Foo(i);
            total += foo.getValue();
        }

        return total;
    }

    public static long test2()
    {
        long total = 0;

        Foo foo = new Foo(0);
        for (long i = 0; i < LOOP; i++)
        {
            foo.setValue(i);
            total += foo.getValue();
        }

        return total;
    }

    public static void main(String[] args)
    {
        long start;

        start = System.currentTimeMillis();
        test1();
        System.out.println(System.currentTimeMillis() - start);

        start = System.currentTimeMillis();
        test2();
        System.out.println(System.currentTimeMillis() - start);
    }
}

Schlussfolgerung: Abhängig von der Größe der lokalen Variablen kann der Unterschied selbst bei nicht so großen Variablen sehr groß sein.

Nur um zu sagen, dass manchmal außerhalb oder innerhalb der Schleife eine Rolle spielt.

RT15
quelle
1
Sicher, die zweite ist schneller, aber Sie machen andere Dinge: test1 erstellt viele Foo-Objekte mit großen Arrays, test2 nicht. test2 verwendet immer wieder dasselbe Foo-Objekt, was in Multithread-Umgebungen gefährlich sein kann.
Hardcoded
Gefährlich in Multithread-Umgebung ??? Bitte erklären Sie warum. Wir sprechen über eine lokale Variable. Es wird bei jedem Aufruf der Methode erstellt.
RT15
Wenn Sie das Foo-Objekt an eine Operation weitergeben, die die Daten asynchron verarbeitet, arbeitet die Operation möglicherweise noch an der Foo-Instanz, während Sie die darin enthaltenen Daten ändern. Es muss nicht einmal Multithreading sein, um Nebenwirkungen zu haben. Die Wiederverwendung von Instanzen ist also ziemlich gefährlich, wenn Sie nicht wissen, wer die Instanz noch verwendet
Hardcoded
Ps: Ihre setValue-Methode sollte sein bigStuff[(int) (value % STUFF_SIZE)] = value;(versuchen Sie einen Wert von 2147483649L)
Hardcoded
Apropos Nebenwirkungen: Haben Sie die Ergebnisse Ihrer Methoden verglichen?
Hardcoded
-1

Ich denke, die Größe des Objekts ist ebenfalls wichtig. In einem meiner Projekte hatten wir ein großes zweidimensionales Array deklariert und initialisiert, wodurch die Anwendung eine Ausnahme wegen Speichermangels auslöste. Wir haben stattdessen die Deklaration aus der Schleife verschoben und das Array zu Beginn jeder Iteration gelöscht.

Sanjit
quelle
-2

Sie haben das Risiko, NullPointerExceptiondass Ihre calculateStr()Methode null zurückgibt und Sie dann versuchen, eine Methode für str aufzurufen.

Vermeiden Sie generell Variablen mit einem Nullwert . Es ist übrigens stärker für Klassenattribute.

Rémi Doolaeghe
quelle
2
Dies hängt in keiner Weise mit der Frage zusammen. Die Wahrscheinlichkeit einer NullPointerException (bei zukünftigen Funktionsaufrufen) hängt nicht davon ab, wie eine Variable deklariert wird.
Wüsteneis
1
Ich glaube nicht, denn die Frage lautet "Was ist der beste Weg, dies zu tun?". IMHO würde ich einen sichereren Code bevorzugen.
Rémi Doolaeghe
1
Es gibt null Risiko eines NullPointerException.Falls dieser Code versucht , return str;es würde ein Übersetzungsfehler auftreten.
Marquis von Lorne