Ich habe eine Schleife, die ungefähr so 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 darin besteht, das Array von Floats zurückzugeben. Ich möchte, dass diese Methode zurückgegeben wird, null
wenn ein Fehler auftritt. Deshalb füge ich die Schleife try...catch
wie folgt in einen Block ein:
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 dachte ich auch daran, den try...catch
Block so in die Schleife zu setzen :
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, eine Leistung oder etwas anderes, einen dem anderen vorzuziehen?
Bearbeiten: Der Konsens scheint zu sein, dass es sauberer ist, die Schleife innerhalb des try / catch zu platzieren, möglicherweise innerhalb seiner eigenen Methode. Es gibt jedoch immer noch Debatten darüber, was schneller geht. Kann jemand dies testen und mit einer einheitlichen Antwort zurückkommen?
quelle
Antworten:
PERFORMANCE:
Es gibt absolut keinen Leistungsunterschied, wo die Try / Catch-Strukturen platziert sind. Intern werden sie als Codebereichstabelle in einer Struktur implementiert, die beim Aufruf der Methode erstellt wird. Während die Methode ausgeführt wird, sind die Try / Catch-Strukturen nicht im Bild, es sei denn, es wird ein Wurf ausgeführt. Anschließend wird der Ort des Fehlers mit der Tabelle verglichen.
Hier ist eine Referenz: http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html
Die Tabelle ist etwa auf halber Höhe beschrieben.
quelle
Leistung : Wie Jeffrey in seiner Antwort sagte, macht es in Java keinen großen Unterschied.
Für die Lesbarkeit des Codes hängt Ihre Wahl, wo die Ausnahme abgefangen werden soll, im Allgemeinen davon ab, ob die Schleife weiter verarbeitet werden soll oder nicht.
In Ihrem Beispiel sind Sie zurückgekehrt, als Sie eine Ausnahme abgefangen haben. In diesem Fall würde ich den Versuch / Fang um die Schleife legen. Wenn Sie einfach einen schlechten Wert abfangen möchten, aber die Verarbeitung fortsetzen möchten, legen Sie ihn ein.
Der dritte Weg : Sie können jederzeit Ihre eigene statische ParseFloat-Methode schreiben und die Ausnahmebehandlung in dieser Methode und nicht in Ihrer Schleife behandeln lassen. Die Ausnahmebehandlung auf die Schleife selbst isolieren!
quelle
Nachdem Jeffrey L Whitledge gesagt hatte, dass es keinen Leistungsunterschied gibt (Stand 1997), habe ich ihn getestet. Ich habe diesen kleinen Benchmark durchgeführt:
Ich habe den resultierenden Bytecode mit javap überprüft, um sicherzustellen, dass nichts inline wurde.
Die Ergebnisse zeigten, dass Jeffrey unter der Annahme unbedeutender JIT-Optimierungen korrekt ist . Es gibt absolut keinen Leistungsunterschied unter Java 6, Sun Client VM (ich hatte keinen Zugriff auf andere Versionen). Die Gesamtzeitdifferenz liegt während des gesamten Tests in der Größenordnung von einigen Millisekunden.
Daher ist die einzige Überlegung, was am saubersten aussieht. Ich finde, dass der zweite Weg hässlich ist, also werde ich mich entweder an den ersten Weg oder an Ray Hayes 'Weg halten .
quelle
Während die Leistung gleich sein mag und das, was besser "aussieht", sehr subjektiv ist, gibt es immer noch einen ziemlich großen Unterschied in der Funktionalität. Nehmen Sie das folgende Beispiel:
Die while-Schleife befindet sich innerhalb des try catch-Blocks. Die Variable 'j' wird inkrementiert, bis sie 40 erreicht. Sie wird ausgedruckt, wenn j mod 4 Null ist, und eine Ausnahme wird ausgelöst, wenn j 20 erreicht.
Vor allen Details hier das andere Beispiel:
Dieselbe Logik wie oben, der einzige Unterschied besteht darin, dass sich der try / catch-Block jetzt innerhalb der while-Schleife befindet.
Hier kommt die Ausgabe (während in try / catch):
Und die andere Ausgabe (try / catch in while):
Da haben Sie einen ganz signifikanten Unterschied:
während in try / catch aus der Schleife ausbricht
try / catch in, während die Schleife aktiv bleibt
quelle
try{} catch() {}
um die Schleife , wenn die Schleife als eine einzige „Einheit“ behandelt wird und die Suche nach einem Problem, dass Einheit der ganze „Einheit“ (oder Schleife in diesem Fall) macht nach Süden gehen. Setzentry{} catch() {}
innerhalb der Schleife , wenn sie eine einzige Iteration der Schleife oder ein einzelnes Element in einem Array als Ganzes „Einheit“ behandeln. Wenn eine Ausnahme in dieser "Einheit" auftritt, überspringen wir einfach dieses Element und fahren dann mit der nächsten Iteration der Schleife fort.Ich bin mit allen Beiträgen zur Leistung und Lesbarkeit einverstanden. Es gibt jedoch Fälle, in denen es wirklich wichtig ist. Ein paar andere Leute erwähnten dies, aber es könnte einfacher sein, anhand von Beispielen zu sehen.
Betrachten Sie dieses leicht modifizierte Beispiel:
Wenn Sie möchten, dass die parseAll () -Methode bei Fehlern (wie im Originalbeispiel) null zurückgibt, setzen Sie den try / catch wie folgt nach außen:
In Wirklichkeit sollten Sie hier wahrscheinlich einen Fehler anstelle von null zurückgeben, und im Allgemeinen mag ich es nicht, mehrere Rückgaben zu haben, aber Sie haben die Idee.
Wenn Sie jedoch möchten, dass die Probleme einfach ignoriert werden und alle möglichen Zeichenfolgen analysiert werden, setzen Sie den Versuch / Fang wie folgt auf die Innenseite der Schleife:
quelle
Wie bereits erwähnt, ist die Leistung gleich. Die Benutzererfahrung ist jedoch nicht unbedingt identisch. Im ersten Fall schlagen Sie schnell fehl (dh nach dem ersten Fehler). Wenn Sie jedoch den try / catch-Block in die Schleife einfügen, können Sie alle Fehler erfassen, die für einen bestimmten Aufruf der Methode erstellt würden. Wenn Sie ein Array von Werten aus Zeichenfolgen analysieren, bei denen Formatierungsfehler zu erwarten sind, gibt es definitiv Fälle, in denen Sie dem Benutzer alle Fehler anzeigen möchten, damit er nicht versuchen muss, sie einzeln zu beheben .
quelle
Wenn alles oder nichts fehlschlägt, ist das erste Format sinnvoll. Wenn Sie alle nicht fehlerhaften Elemente verarbeiten / zurückgeben möchten, müssen Sie das zweite Formular verwenden. Das wären meine grundlegenden Kriterien für die Wahl zwischen den Methoden. Persönlich würde ich, wenn es alles oder nichts ist, die zweite Form nicht verwenden.
quelle
Solange Sie wissen, was Sie in der Schleife erreichen müssen, können Sie den try catch außerhalb der Schleife platzieren. Es ist jedoch wichtig zu verstehen, dass die Schleife dann endet, sobald die Ausnahme auftritt, und dass dies möglicherweise nicht immer das ist, was Sie möchten. Dies ist tatsächlich ein sehr häufiger Fehler in Java-basierter Software. Die Benutzer müssen eine Reihe von Elementen verarbeiten, z. B. das Leeren einer Warteschlange, und sich fälschlicherweise auf eine äußere try / catch-Anweisung verlassen, die alle möglichen Ausnahmen behandelt. Sie könnten auch nur eine bestimmte Ausnahme innerhalb der Schleife behandeln und keine andere Ausnahme erwarten. Wenn dann eine Ausnahme auftritt, die nicht innerhalb der Schleife behandelt wird, wird die Schleife "vorbelegt", sie endet möglicherweise vorzeitig und die äußere catch-Anweisung behandelt die Ausnahme.
Wenn die Schleife im Leben die Aufgabe hatte, eine Warteschlange zu leeren, könnte diese Schleife sehr wahrscheinlich enden, bevor diese Warteschlange wirklich geleert wurde. Sehr häufiger Fehler.
quelle
In Ihren Beispielen gibt es keinen funktionalen Unterschied. Ich finde Ihr erstes Beispiel besser lesbar.
quelle
Sie sollten die äußere Version der inneren Version vorziehen. Dies ist nur eine bestimmte Version der Regel. Verschieben Sie alles außerhalb der Schleife, was Sie außerhalb der Schleife verschieben können. Abhängig vom IL-Compiler und JIT-Compiler können Ihre beiden Versionen unterschiedliche Leistungsmerkmale aufweisen oder nicht.
In einem anderen Punkt sollten Sie sich wahrscheinlich float.TryParse oder Convert.ToFloat ansehen.
quelle
Wenn Sie den Versuch / Fang in die Schleife einfügen, wird die Schleife nach einer Ausnahme fortgesetzt. Wenn Sie es außerhalb der Schleife platzieren, werden Sie angehalten, sobald eine Ausnahme ausgelöst wird.
quelle
Meine Perspektive wäre, dass Try / Catch-Blöcke notwendig sind, um eine ordnungsgemäße Ausnahmebehandlung sicherzustellen, aber das Erstellen solcher Blöcke hat Auswirkungen auf die Leistung. Da Schleifen intensive sich wiederholende Berechnungen enthalten, wird nicht empfohlen, Try / Catch-Blöcke in Schleifen einzufügen. Außerdem scheint es, dass dort, wo dieser Zustand auftritt, häufig "Exception" oder "RuntimeException" abgefangen wird. RuntimeException, die im Code abgefangen wird, sollte vermieden werden. Wenn Sie in einem großen Unternehmen arbeiten, ist es wichtig, diese Ausnahme ordnungsgemäß zu protokollieren oder die Laufzeitausnahme zu stoppen. Der springende Punkt dieser Beschreibung ist
PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS
quelle
Das Einrichten eines speziellen Stapelrahmens für Try / Catch erhöht den Overhead, aber die JVM kann möglicherweise die Tatsache erkennen, dass Sie zurückkehren, und diesen optimieren.
Abhängig von der Anzahl der Iterationen ist der Leistungsunterschied wahrscheinlich vernachlässigbar.
Ich stimme jedoch den anderen zu, dass der Loop-Körper sauberer aussieht, wenn er außerhalb der Schleife liegt.
Wenn die Möglichkeit besteht, dass Sie jemals mit der Verarbeitung fortfahren möchten, anstatt sie zu beenden, wenn eine ungültige Nummer vorliegt, möchten Sie, dass sich der Code in der Schleife befindet.
quelle
Wenn es innen ist, erhalten Sie den Overhead der Try / Catch-Struktur N-mal, im Gegensatz zu nur einmal außen.
Jedes Mal, wenn eine Try / Catch-Struktur aufgerufen wird, erhöht sich der Aufwand für die Ausführung der Methode. Nur ein bisschen Speicher- und Prozessor-Ticks, um mit der Struktur fertig zu werden. Wenn Sie eine Schleife 100 Mal ausführen und hypothetisch gesehen, nehmen wir an, dass die Kosten 1 Tick pro Try / Catch-Aufruf betragen. Wenn Sie Try / Catch in der Schleife haben, kosten Sie 100 Ticks, im Gegensatz zu nur 1 Tick, wenn dies der Fall ist außerhalb der Schleife.
quelle
Der springende Punkt bei Ausnahmen ist, den ersten Stil zu fördern: Die Fehlerbehandlung einmal konsolidieren und behandeln zu lassen, nicht sofort an jeder möglichen Fehlerstelle.
quelle
tue es hinein. Sie können die Verarbeitung fortsetzen (wenn Sie möchten) oder eine hilfreiche Ausnahme auslösen, die dem Client den Wert von myString und den Index des Arrays mit dem fehlerhaften Wert mitteilt. Ich denke, NumberFormatException wird Ihnen bereits den schlechten Wert mitteilen, aber das Prinzip besteht darin, alle hilfreichen Daten in die Ausnahmen zu setzen, die Sie auslösen. Überlegen Sie, was Sie an dieser Stelle im Programm im Debugger interessieren würde.
Erwägen:
In Zeiten der Not werden Sie eine Ausnahme wie diese mit möglichst vielen Informationen wirklich zu schätzen wissen.
quelle
Ich möchte meine eigenen
0.02c
zwei konkurrierenden Überlegungen hinzufügen, wenn ich das allgemeine Problem betrachte, wo die Ausnahmebehandlung positioniert werden soll:Die "größere" Verantwortung des
try-catch
Blocks (dh in Ihrem Fall außerhalb der Schleife) bedeutet, dass Sie beim Ändern des Codes zu einem späteren Zeitpunkt möglicherweise fälschlicherweise eine Zeile hinzufügen, die von Ihrem vorhandenencatch
Block behandelt wird. möglicherweise unbeabsichtigt. In Ihrem Fall ist dies weniger wahrscheinlich, da Sie explizit a fangenNumberFormatException
Je "enger" die Verantwortung der
try-catch
Blocks ist, desto schwieriger wird das Refactoring. Insbesondere, wenn Sie (wie in Ihrem Fall) eine "nicht lokale" Anweisung aus demcatch
Block (derreturn null
Anweisung) ausführen .quelle
Das hängt von der Fehlerbehandlung ab. Wenn Sie nur die Fehlerelemente überspringen möchten, versuchen Sie es innen:
In jedem anderen Fall würde ich den Versuch draußen vorziehen. Der Code ist besser lesbar und sauberer. Vielleicht ist es besser, stattdessen eine IllegalArgumentException in den Fehlerfall zu werfen, wenn null zurückgegeben wird.
quelle
Ich werde meine $ 0,02 eingeben. Manchmal müssen Sie später ein "endlich" in Ihren Code einfügen (denn wer schreibt den Code jemals beim ersten Mal perfekt?). In diesen Fällen ist es plötzlich sinnvoller, den Versuch / Fang außerhalb der Schleife zu haben. Beispielsweise:
Denn wenn Sie eine Fehlermeldung erhalten oder nicht, möchten Sie Ihre Datenbankverbindung nur einmal freigeben (oder Ihren bevorzugten Typ einer anderen Ressource auswählen ...).
quelle
Ein weiterer Aspekt, der oben nicht erwähnt wurde, ist die Tatsache, dass jeder Try-Catch einen gewissen Einfluss auf den Stack hat, was Auswirkungen auf rekursive Methoden haben kann.
Wenn die Methode "Outer ()" die Methode "Inner ()" aufruft (die sich möglicherweise rekursiv aufruft), versuchen Sie, den Try-Catch nach Möglichkeit in der Methode "Outer ()" zu finden. Ein einfaches Beispiel für einen "Stapelabsturz", das wir in einer Leistungsklasse verwenden, schlägt bei etwa 6.400 Frames fehl, wenn sich der Try-Catch in der inneren Methode befindet, und bei etwa 11.600 Frames, wenn er sich in der äußeren Methode befindet.
In der realen Welt kann dies ein Problem sein, wenn Sie das zusammengesetzte Muster verwenden und große, komplexe verschachtelte Strukturen haben.
quelle
Wenn Sie Exception für jede Iteration abfangen oder überprüfen möchten, bei welcher Iteration Exception ausgelöst wird, und alle Exceptions in einer itertaion abfangen möchten, platzieren Sie try ... catch in der Schleife. Dadurch wird die Schleife nicht unterbrochen, wenn eine Ausnahme auftritt, und Sie können jede Ausnahme in jeder Iteration in der gesamten Schleife abfangen.
Wenn Sie die Schleife unterbrechen und die Ausnahme bei jedem Auslösen untersuchen möchten, verwenden Sie try ... catch out of the loop. Dadurch wird die Schleife unterbrochen und Anweisungen nach catch (falls vorhanden) ausgeführt.
Es hängt alles von Ihren Bedürfnissen ab. Ich bevorzuge die Verwendung von try ... catch innerhalb der Schleife während der Bereitstellung, da die Ergebnisse im Falle einer Ausnahme nicht mehrdeutig sind und die Schleife nicht unterbrochen und vollständig ausgeführt wird.
quelle