Frage

Ich habe eine Schleife, die etwa wie folgt aussieht:

for (int i = 0; i < max; i++) {
    String myString = ...;
    float myNum = Float.parseFloat(myString);
    myFloats[i] = myNum;
}

Dies ist der Hauptinhalt einer Methode, deren einziger Zweck es ist, die Anordnung der Schwimmer zurückzukehren. Ich möchte diese Methode null zurück, wenn ein Fehler auftritt, so dass ich die Schleife in einem try...catch Block, wie folgt aus:

try {
    for (int i = 0; i < max; i++) {
        String myString = ...;
        float myNum = Float.parseFloat(myString);
        myFloats[i] = myNum;
    }
} catch (NumberFormatException ex) {
    return null;
}

Aber dann auch dachte ich an den try...catch Block innerhalb der Schleife setzen, wie folgt aus:

for (int i = 0; i < max; i++) {
    String myString = ...;
    try {
        float myNum = Float.parseFloat(myString);
    } catch (NumberFormatException ex) {
        return null;
    }
    myFloats[i] = myNum;
}

Gibt es einen Grund, Leistung oder auf andere Weise, einen über den anderen zu bevorzugen?


Edit: Der Konsens scheint zu sein, dass es sauberer ist die Schleife innerhalb des try / catch zu setzen, möglicherweise innerhalb der eigenen Methode. Allerdings ist immer noch da Debatte über die schneller ist. Kann dies jemand testen und mit einer einheitlichen Antwort zurückkommen?

War es hilfreich?

Lösung 3

In Ordnung, nach Jeffrey L Whitledge sagte , dass es keinen Unterschied in der Leistung (Stand: 1997) war, ging ich und getestet. Ich lief diesen kleinen Maßstab:

public class Main {

    private static final int NUM_TESTS = 100;
    private static int ITERATIONS = 1000000;
    // time counters
    private static long inTime = 0L;
    private static long aroundTime = 0L;

    public static void main(String[] args) {
        for (int i = 0; i < NUM_TESTS; i++) {
            test();
            ITERATIONS += 1; // so the tests don't always return the same number
        }
        System.out.println("Inside loop: " + (inTime/1000000.0) + " ms.");
        System.out.println("Around loop: " + (aroundTime/1000000.0) + " ms.");
    }
    public static void test() {
        aroundTime += testAround();
        inTime += testIn();
    }
    public static long testIn() {
        long start = System.nanoTime();
        Integer i = tryInLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static long testAround() {
        long start = System.nanoTime();
        Integer i = tryAroundLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static Integer tryInLoop() {
        int count = 0;
        for (int i = 0; i < ITERATIONS; i++) {
            try {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            } catch (NumberFormatException ex) {
                return null;
            }
        }
        return count;
    }
    public static Integer tryAroundLoop() {
        int count = 0;
        try {
            for (int i = 0; i < ITERATIONS; i++) {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            }
            return count;
        } catch (NumberFormatException ex) {
            return null;
        }
    }
}

Ich habe die resultierende Bytecode sicher machen mit javap, dass nichts inlined wurde.

Die Ergebnisse zeigten, dass unbedeutende JIT-Optimierungen unter der Annahme, Jeffrey ist richtig ; gibt es absolut keinen Unterschied in der Leistung auf Java 6, Sun Client VM (Ich habe keinen Zugang zu anderen Versionen). Die Gesamtzeitdifferenz liegt in der Größenordnung von wenigen Millisekunden über den gesamten Test.

Daher ist die einzige Überlegung, was aussieht saubersten. Ich finde, dass der zweite Weg hässlich ist, also werde ich entweder auf dem ersten Weg oder Ray Hayes Art und Weise .

Andere Tipps

PERFORMANCE:

Es gibt absolut keinen Unterschied in der Leistung in dem die try / catch Strukturen platziert werden. Intern werden sie als Code-Bereichstabelle in einer Struktur realisiert, die erstellt wird, wenn das Verfahren aufgerufen wird. Während das Verfahren ausgeführt wird, sind die try / catch-Strukturen vollständig aus dem Bild heraus, es sei denn ein Wurf auftritt, dann wird die Position des Fehlers im Vergleich gegen den Tisch.

Hier ist eine Referenz: http: // www. javaworld.com/javaworld/jw-01-1997/jw-01-hood.html

Die Tabelle etwa auf halbem Weg beschrieben wird, nach unten.

Performance : wie Jeffrey in seiner Antwort sagte, in Java ist es nicht machen viel Unterschied.

Allgemein , um die Lesbarkeit des Codes Ihre Wahl, wo die Ausnahme zu fangen, hängt davon ab, ob Sie die Schleife wollen Verarbeitung zu halten oder nicht.

In Ihrem Beispiel zurückgegeben Sie eine Ausnahme auf zu kontrollieren. In diesem Fall würde ich die try / catch um die Schleife setzen. Wenn Sie einfach nur einen schlechten Wert fangen wollen aber weitermachen Verarbeitung, legt es in.

Der dritte Weg, : Sie können immer Ihre eigene statische parseFloat Methode schreiben und die Ausnahmebehandlung mit in dieser Methode behandelt haben, anstatt Ihre Schleife. Herstellung der Ausnahmebehandlung in die Schleife isoliert selbst!

class Parsing
{
    public static Float MyParseFloat(string inputValue)
    {
        try
        {
            return Float.parseFloat(inputValue);
        }
        catch ( NumberFormatException e )
        {
            return null;
        }
    }

    // ....  your code
    for(int i = 0; i < max; i++) 
    {
        String myString = ...;
        Float myNum = Parsing.MyParseFloat(myString);
        if ( myNum == null ) return;
        myFloats[i] = (float) myNum;
    }
}

Während Leistung könnte die gleiche sein, und was „schaut“ besser ist sehr subjektiv, gibt es noch einen ziemlich großen Unterschied in der Funktionalität. Nehmen Sie das folgende Beispiel:

Integer j = 0;
    try {
        while (true) {
            ++j;

            if (j == 20) { throw new Exception(); }
            if (j%4 == 0) { System.out.println(j); }
            if (j == 40) { break; }
        }
    } catch (Exception e) {
        System.out.println("in catch block");
    }

Die while-Schleife ist im Inneren des try catch-Block, die Variable ‚j‘ wird inkrementiert, bis er 40 trifft, ausgedruckt, wenn j mod 4 Null ist und eine Ausnahme ausgelöst wird, wenn j 20 trifft.

Bevor irgendwelche Details, hier dem anderen Beispiel:

Integer i = 0;
    while (true) {
        try {
            ++i;

            if (i == 20) { throw new Exception(); }
            if (i%4 == 0) { System.out.println(i); }
            if (i == 40) { break; }

        } catch (Exception e) { System.out.println("in catch block"); }
    }

Die gleiche Logik wie oben einziger Unterschied besteht darin, dass die try / catch-Block jetzt innerhalb der while-Schleife ist.

Hier kommt die Ausgabe (während in try / catch):

4
8
12 
16
in catch block

Und der andere Ausgang (try / catch in while):

4
8
12
16
in catch block
24
28
32
36
40

Da haben Sie einen ganz wesentlichen Unterschied:

, während in try / catch bricht aus der Schleife

try / catch in während hält die Schleife aktiv

Ich stimme mit allen Leistungen und Lesbarkeit Beiträge. Es gibt jedoch Fälle, in denen es wirklich keine Rolle. Ein paar andere Leute erwähnt dies, aber es könnte einfacher sein, mit Beispielen zu sehen.

Betrachten Sie diese leicht modifiziertes Beispiel:

public static void main(String[] args) {
    String[] myNumberStrings = new String[] {"1.2345", "asdf", "2.3456"};
    ArrayList asNumbers = parseAll(myNumberStrings);
}

public static ArrayList parseAll(String[] numberStrings){
    ArrayList myFloats = new ArrayList();

    for(int i = 0; i < numberStrings.length; i++){
        myFloats.add(new Float(numberStrings[i]));
    }
    return myFloats;
}

Wenn Sie wollen, dass die parseAll () Methode null zurück, wenn es irgendwelche Fehler ist (wie das Original Beispiel), dann würden Sie das try / catch auf der Außenseite so:

public static ArrayList parseAll1(String[] numberStrings){
    ArrayList myFloats = new ArrayList();
    try{
        for(int i = 0; i < numberStrings.length; i++){
            myFloats.add(new Float(numberStrings[i]));
        }
    } catch (NumberFormatException nfe){
        //fail on any error
        return null;
    }
    return myFloats;
}

In Wirklichkeit sollten Sie vielleicht einen Fehler zurück, hier anstelle von null, und in der Regel mag ich nicht mehrere kehrt zu haben, aber Sie bekommen die Idee.

Auf der anderen Seite, wenn Sie wollen, dass es nur um die Probleme zu ignorieren, und analysiert, was Strings können, können Sie das try / catch auf der Innenseite der Schleife so sagen würden:

public static ArrayList parseAll2(String[] numberStrings){
    ArrayList myFloats = new ArrayList();

    for(int i = 0; i < numberStrings.length; i++){
        try{
            myFloats.add(new Float(numberStrings[i]));
        } catch (NumberFormatException nfe){
            //don't add just this one
        }
    }

    return myFloats;
}

Wie bereits erwähnt, ist die Leistung gleich. Allerdings ist User Experience nicht notwendigerweise identisch. Im ersten Fall, werden Sie scheitern schnell (das heißt nach dem ersten Fehler), aber wenn Sie den try / catch-Block innerhalb der Schleife setzen, können Sie alle Fehler erfassen, die für einen bestimmten Aufruf der Methode erstellt werden würde. Wenn ein Array von Werten von Strings Parsen, wo Sie einige Formatierungsfehler erwarten, gibt es definitiv Fälle, in denen Sie in der Lage sein möchten alle Fehler an den Benutzer zu präsentieren, damit sie brauchen, um sie nicht zu versuchen und zu beheben eins nach dem anderen .

Wenn sein ein Alles-oder-nichts scheitern, dann wird das erste Format macht Sinn. Wenn Sie in der Lage sein zu verarbeiten wollen / Return alle nicht versagenden Elemente, müssen Sie das zweite Formular verwenden. Das wäre meine grundlegende Kriterien sein, zwischen den Methoden der Wahl. Persönlich, wenn es alles oder nichts ist, würde ich nicht das zweite Formular.

Solange Sie wissen, was Sie in der Schleife erreichen müssen Sie den Versuch fangen außerhalb der Schleife setzen könnte. Aber es ist wichtig zu verstehen, dass die Schleife wird dann beenden, sobald die Ausnahme auftritt und dass nicht immer das, was Sie wollen. Dies ist tatsächlich ein sehr häufigen Fehler in Java-basierter Software. Die Menschen müssen eine Reihe von Punkten, wie Entleerung eine Warteschlange, und fälschlicherweise verlassen sich auf einer äußeren try / catch-Anweisung verarbeiten alle möglichen Ausnahmen Handhabung. Sie könnten auch nur eine spezifische Ausnahme innerhalb der Schleife der Handhabung sein und erwarten keine andere Ausnahme auftreten. Dann, wenn eine Ausnahme auftritt, das nicht innerhalb der Schleife behandelt, dann wird die Schleife „preemted“ werden, es endet möglicherweise vorzeitig und die äußere catch-Anweisung behandelt die Ausnahme.

Wenn die Schleife als seine Rolle im Leben hat eine Warteschlange zu leeren, dass die Schleife dann sehr wahrscheinlich enden könnte, bevor die Warteschlange wirklich geleert wurde. Sehr häufig Fehler.

In Ihren Beispielen gibt es keinen funktionalen Unterschied. Ich finde Ihr erstes Beispiel besser lesbar.

Sie sollten die Außen Version über die innere Version bevorzugen. Dies ist nur eine bestimmte Version der Regel etwas außerhalb der Schleife bewegen, die Sie außerhalb der Schleife bewegen können. In Abhängigkeit von dem IL-Compiler und JIT-Compiler Ihrer zwei Versionen am Ende können oder auch nicht mit unterschiedlichen Leistungsmerkmalen.

Auf einer anderen Anmerkung Sie wahrscheinlich float.TryParse oder Convert.ToFloat aussehen sollten.

Wenn Sie die try / catch in der Schleife setzen, werden Sie behalten nach einer Ausnahme Looping. Wenn Sie es außerhalb der Schleife gesetzt werden Sie, sobald eine Ausnahme ausgelöst stoppen wird.

Meine Perspektive würde try / catch-Blöcke notwendig sind, um die richtige Ausnahmebehandlung zu gewährleisten, sondern die Schaffung solcher Blöcke Auswirkungen auf die Leistung hat. Da Loops intensive wiederholende Berechnungen enthält, ist es nicht zu setzen empfohlen try / catch-Blöcke innerhalb von Schleifen. Darüber hinaus scheint es, wo dieser Zustand eintritt, ist es oft „Exception“ oder „Runtime“, die gefangen wird. Runtime wird in Code gefangen sollte vermieden werden. Noch einmal, wenn, wenn Sie in einem großen Unternehmen zu arbeiten ist es wichtig, dass die Ausnahme richtig zu protokollieren oder Laufzeitausnahme zu stoppen passieren. Springende Punkt dieser Beschreibung wird PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS

einen speziellen Stapelrahmen für die try / catch fügt zusätzlichen Aufwand einrichten, aber die JVM kann die Tatsache erkennen kann, dass Sie zurückkehren und optimiert diese weg.

in Abhängigkeit von der Anzahl der Iterationen, Performance-Unterschied wahrscheinlich vernachlässigbar sein wird.

Jedoch habe ich mit den anderen darüber einig, dass es außerhalb der Schleife macht den Schleifenkörper sehen Reiniger mit.

Wenn es eine Chance, dass Sie immer mit der Verarbeitung anstatt Ausgang weiterhin auf werden wollen, wenn es eine ungültige Nummer, dann würden Sie den Code wollen innerhalb der Schleife sein.

Wenn es drin ist, dann werden Sie den Aufwand für die try / catch-Struktur N-mal gewinnen, im Gegensatz zu nur die einmal auf der Außenseite.


Jedes Mal, wenn ein Try / Catch Struktur fügt Overhead für die Ausführung der Methode aufgerufen wird. Gerade die wenig Speicher und Prozessor Ticks benötigt mit der Struktur befassen. Wenn Sie eine Schleife 100 mal laufen lassen, und für hypothetische willen, sagen wir, die Kosten 1 tick pro try / catch Anruf, dann das Try / Catch mit innerhalb der Schleife kostet Sie 100 Ticks, im Gegensatz zu nur 1 tick, wenn es außerhalb der Schleife.

Der ganze Sinn der Ausnahme ist die erste Art zu fördern. Die Fehlerbehandlung konsolidierte und einmal behandelt werden zu lassen, nicht sofort bei jeder möglichen Fehler Website

es nach innen setzen. Sie können die Verarbeitung halten (wenn Sie möchten), oder Sie können eine hilfreiche Ausnahme auslösen, dass der Kunde den Wert von myString und den Index des Array mit den schlechten Wert teilt. Ich denke, Number wird man schon sagen, den schlechten Wert, aber das Prinzip ist es, alle hilfreich Daten in den Ausnahmen zu stellen, die Sie werfen. Denken Sie darüber nach, was an dieser Stelle im Programm im Debugger Sie wäre interessant.

Bedenken Sie:

try {
   // parse
} catch (NumberFormatException nfe){
   throw new RuntimeException("Could not parse as a Float: [" + myString + 
                              "] found at index: " + i, nfe);
} 

In der Zeit der Not werden Sie schätzen eine Ausnahme wie dies wirklich so viele Informationen in ihm wie möglich.

I bin wie mein eigenen 0.02c über zwei konkurrierenden Überlegungen hinzufügen, wenn auf dem allgemeinen Problem der, wo die Ausnahmebehandlung positionieren:

  1. Die „breite“ in der Verantwortung des try-catch Blockes (dh außerhalb der Schleife in Ihrem Fall) bedeutet, dass, wenn Sie den Code zu einem späteren Zeitpunkt zu ändern, Sie versehentlich eine Zeile hinzufügen können, die von Ihrem vorhandenen catch Block behandelt wird ; möglicherweise unbeabsichtigt. In Ihrem Fall ist dies weniger wahrscheinlich, weil Sie explizit sind ein NumberFormatException Fang

  2. Die "engere" in der Verantwortung des try-catch Blockes ist, desto schwieriger Refactoring wird. Insbesondere dann, wenn (wie in Ihrem Fall) Sie ausführen eine „nicht-lokale“ Anweisung von innerhalb des catch Blockes (die return null Anweisung).

Das hängt von der Fehlerbehandlung. Wenn Sie nur die Fehlerelemente überspringen möchten, prüfen Sie:

for(int i = 0; i < max; i++) {
    String myString = ...;
    try {
        float myNum = Float.parseFloat(myString);
        myFloats[i] = myNum;
    } catch (NumberFormatException ex) {
        --i;
    }
}

In jedem anderen Fall würde ich die Außen versuchen bevorzugen. Der Code ist besser lesbar, so dass es sauber ist. Vielleicht wäre es besser, eine Illegal im Fehlerfall zu werfen, anstatt null, wenn zurückkehren.

Ich werde meinen $ 0,02 in. Manchmal ein „endlich“ im Code später benötigen Sie aufzuwickeln hinzufügen (weil die jemals ihren Code perfekt zum ersten Mal schreiben?). In diesen Fällen macht es plötzlich mehr Sinn, die try / catch außerhalb der Schleife zu haben. Zum Beispiel:

try {
    for(int i = 0; i < max; i++) {
        String myString = ...;
        float myNum = Float.parseFloat(myString);
        dbConnection.update("MY_FLOATS","INDEX",i,"VALUE",myNum);
    }
} catch (NumberFormatException ex) {
    return null;
} finally {
    dbConnection.release();  // Always release DB connection, even if transaction fails.
}

Weil, wenn Sie einen Fehler, oder nicht, mögen Sie nur Ihre Datenbankverbindung lösen (oder Ihre bevorzugte Art von anderen Ressourcen auswählen ...) einmal.

Ein weiterer Aspekt, der nicht in der oben erwähnten, ist die Tatsache, dass jeder versuchen Beifang hat einige Auswirkungen auf den Stapel, die Auswirkungen auf die rekursive Methoden haben kann.

Wenn Methode "outer ()" Anrufe Methode "innere ()" (die selbst rekursiv aufrufen kann), versuchen Sie die Try-Catch-in-Verfahren "Außen ()", wenn möglich, zu lokalisieren. Ein einfaches „Stack Crash“ Beispiel, das wir in einer Leistungsklasse verwenden versagt bei etwa 6.400 Frames, wenn der Try-Catch in der inneren Methode ist, und bei etwa 11.600, wenn es in der äußeren Methode.

In der realen Welt kann dies ein Problem sein, wenn Sie das Composite-Muster verwenden und haben große, komplexe verschachtelte Strukturen.

Wenn Sie Ausnahme für jede Iteration fangen wollen, oder versuchen Sie es zu, was Iteration Ausnahme ausgelöst und alle Ausnahmen in einer itertaion zu fangen, Ort versuchen ... in der Schleife zu fangen. Dies wird nicht bricht die Schleife, wenn Ausnahme auftritt und Sie können jede Ausnahme in jeder Iteration in der gesamten Schleife fangen.

Wenn Sie die Schleife brechen wollen und die Exception zu untersuchen, wenn geworfen, verwenden Sie versuchen ... aus der Schleife zu fangen. Dadurch wird die Schleife verlassen und führen Anweisungen nach catch (falls vorhanden).

Es hängt alles von Ihren Bedürfnissen. Ich ziehe mit try ... innerhalb der Schleife fangen, während als bereitstellen, wenn Ausnahme auftritt, sind die Ergebnisse nicht eindeutig sind und Schleife wird nicht brechen, und führen Sie vollständig aus.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top