Spróbuj ... złapać go wewnątrz czy na zewnątrz pętli?
Mam pętlę, która wygląda mniej więcej tak:
for (int i = 0; i < max; i++) {
String myString = ...;
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
}
Jest to główna Zawartość metody, której jedynym celem jest zwrócenie tablicy pływaków. Chcę, aby ta metoda zwróciła null
, jeśli wystąpi błąd, więc umieszczam pętlę wewnątrz try...catch
bloku, Tak:
try {
for (int i = 0; i < max; i++) {
String myString = ...;
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
}
} catch (NumberFormatException ex) {
return null;
}
Ale wtedy też pomyślałem o umieszczeniu try...catch
bloku wewnątrz pętli, Tak:
for (int i = 0; i < max; i++) {
String myString = ...;
try {
float myNum = Float.parseFloat(myString);
} catch (NumberFormatException ex) {
return null;
}
myFloats[i] = myNum;
}
Czy Jest jakiś powód, wydajność lub w inny sposób, aby wolą jeden niż inne?
Edit: konsensus wydaje się być taki, że czystsze jest umieszczenie pętli wewnątrz try/catch, ewentualnie wewnątrz własnej metody. Jednak nadal trwa debata, która jest szybsza. Czy ktoś może to przetestować i wrócić z ujednoliconą odpowiedzią?
21 answers
Wydajność:
Nie ma absolutnie żadnej różnicy w wydajności w miejscu, w którym znajdują się struktury try/catch. Wewnętrznie są one zaimplementowane jako tabela zakresu kodu w strukturze, która jest tworzona podczas wywoływania metody. Podczas wykonywania metody, struktury try/catch są całkowicie poza obrazem, chyba że wystąpi rzut, a następnie lokalizacja błędu jest porównywana z tabelą.
Oto odniesienie: http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html
Tabela jest opisana w połowie drogi w dół.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 20:17:43
Wydajność : Jak Jeffrey powiedział w swojej odpowiedzi, w Javie to nie robi wielkiej różnicy.
Ogólnie , dla czytelności kodu, wybór miejsca przechwycenia wyjątku zależy od tego, czy chcesz, aby pętla nadal przetwarzała, czy nie.
W twoim przykładzie powróciłeś po złapaniu wyjątku. W takim razie, chciałbym umieścić spróbować / złapać wokół pętli. Jeśli po prostu chcesz złapać złą wartość, ale kontynuuj przetwarzanie, włóż ją do środka.
Trzeci sposób: zawsze możesz napisać własną statyczną metodę ParseFloat i mieć do czynienia z obsługą WYJĄTKÓW w tej metodzie, a nie pętli. Czyniąc obsługę wyjątków odizolowanymi do samej pętli!
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;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:24
W porządku, po tym jak Jeffrey L Whitledge powiedział, że nie ma różnicy w wydajności (od 1997 roku), poszedłem ją przetestować. Sprawdziłem ten mały benchmark:
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;
}
}
}
Sprawdziłem uzyskany bajt za pomocą javap, aby upewnić się, że nic nie zostało zainlinowane.
Wyniki pokazały, że zakładając nieznaczne optymalizacje JIT, Jeffrey jest poprawny ; nie ma absolutnie żadnej różnicy w wydajności na Java 6, Sun client VM (nie miałem dostępu do innych wersji). Całkowita różnica czasu jest rzędu kilku milisekund w całym teście.
Dlatego jedynym rozważaniem jest to, co wygląda najczystiej. Uważam, że druga droga jest brzydka, więc będę trzymać się pierwszej drogi lub drogi Raya Hayesa .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:22
Chociaż wydajność może być taka sama, a to, co" wygląda " lepiej, jest bardzo subiektywne, nadal istnieje dość duża różnica w funkcjonalności. Weźmy następujący przykład:
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");
}
Pętla while znajduje się wewnątrz bloku try catch, zmienna 'j' jest zwiększana do 40, wypisywana, gdy J mod 4 wynosi zero, a wyjątek jest wyrzucany, gdy J trafi 20.
Przed jakimikolwiek szczegółami, tutaj drugi przykład:
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"); }
}
Ta sama logika jak powyżej, jedyna różnica polega na tym, że try/catch block jest teraz wewnątrz pętli while.
Oto wyjście (podczas try/catch):
4
8
12
16
in catch block
I inne wyjście (try/catch in while):
4
8
12
16
in catch block
24
28
32
36
40
Tam masz dość znaczącą różnicę:
Gdy w try / catch wyłamuje się z pętli
Try / catch in while keeps the loop active
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-30 12:27:33
Zgadzam się ze wszystkimi postami o wydajności i czytelności. Są jednak przypadki, w których to naprawdę ma znaczenie. Kilka innych osób wspomniało o tym, ale łatwiej byłoby to zobaczyć na przykładach.
Rozważmy ten nieco zmodyfikowany przykład:
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;
}
Jeśli chcesz, aby metoda parseAll() zwracała null, jeśli są jakieś błędy( jak w oryginalnym przykładzie), powinieneś umieścić try / catch Na zewnątrz w następujący sposób:
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;
}
W rzeczywistości, prawdopodobnie powinieneś zwrócić błąd tutaj zamiast null, i ogólnie nie lubię mieć wielu zwrotów, ale masz pomysł.
Z drugiej strony, jeśli chcesz, aby po prostu zignorowała problemy i przeanalizowała wszystkie możliwe ciągi, umieścisz try / catch na wewnętrznej stronie pętli w następujący sposób:
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-24 19:59:04
Jak już wspomniano, wydajność jest taka sama. Jednak doświadczenie użytkownika niekoniecznie jest identyczne. W pierwszym przypadku szybko się nie uda( tj. po pierwszym błędzie), jednak jeśli umieścisz blok try/catch wewnątrz pętli, możesz przechwycić wszystkie błędy, które zostaną utworzone dla danego wywołania metody. Podczas parsowania tablicy wartości z łańcuchów, w których spodziewasz się błędów formatowania, są zdecydowanie przypadki, w których chciałbyś być w stanie przedstawić wszystkie błędy użytkownika, aby nie musieli próbować naprawiać ich jeden po drugim.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-13 01:22:16
Jeśli to wszystko albo nic, to pierwszy format ma sens. Jeśli chcesz być w stanie przetworzyć/zwrócić wszystkie niedziałające elementy, musisz użyć drugiego formularza. To byłyby moje podstawowe kryteria wyboru pomiędzy metodami. Osobiście, jeśli to wszystko albo nic, nie używałbym drugiej formy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 19:59:38
Dopóki jesteś świadomy tego, co musisz osiągnąć w pętli, możesz umieścić try catch poza pętlą. Ale ważne jest, aby zrozumieć, że pętla zakończy się, gdy tylko wystąpi wyjątek i to nie zawsze może być to, czego chcesz. Jest to w rzeczywistości bardzo częsty błąd w oprogramowaniu opartym na Javie. Ludzie muszą przetwarzać wiele elementów, takich jak opróżnianie kolejki, i fałszywie polegać na zewnętrznej instrukcji try/catch obsługującej wszystkie możliwe wyjątki. Mogą też obsługiwać tylko określony wyjątek wewnątrz pętli i nie należy oczekiwać wystąpienia żadnego innego wyjątku. Następnie, jeśli wystąpi wyjątek, który nie jest obsługiwany wewnątrz pętli, pętla zostanie "preemted", kończy się prawdopodobnie przedwcześnie i instrukcja outer catch obsługuje wyjątek.
Jeśli pętla miała za swoją rolę w życiu opróżnić kolejkę, to pętla prawdopodobnie mogłaby się skończyć, zanim kolejka ta została naprawdę opróżniona. Bardzo częsty błąd.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-31 09:08:33
W Twoich przykładach nie ma różnicy funkcjonalnej. Twój pierwszy przykład jest bardziej czytelny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 19:55:34
Powinieneś preferować wersję zewnętrzną niż wewnętrzną. Jest to tylko konkretna wersja reguły, przenieś wszystko poza pętlę, które możesz przenieść poza pętlę. W zależności od kompilatora IL i kompilatora JIT dwie wersje mogą, ale nie muszą, mieć różne charakterystyki wydajności.
W innej notce powinieneś chyba spojrzeć na float.Wypróbuj lub przekonwertuj.ToFloat.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 19:57:18
Jeśli umieścisz try / catch wewnątrz pętli, będziesz nadal zapętlać po wyjątku. Jeśli umieścisz go poza pętlą, zatrzymasz się zaraz po wyrzuceniu wyjątku.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 19:58:23
Moim zdaniem bloki try/catch są niezbędne do zapewnienia właściwej obsługi wyjątków, ale tworzenie takich bloków ma wpływ na wydajność. Ponieważ pętle zawierają intensywne powtarzalne obliczenia, nie zaleca się umieszczania bloków try/catch wewnątrz pętli. Ponadto wydaje się, że tam, gdzie występuje ten warunek, często jest to" wyjątek "lub" RuntimeException", który jest przechwytywany. Należy unikać przechwytywania RuntimeException w kodzie. Ponownie, jeśli pracujesz w dużej firmie, konieczne jest, aby zapisz ten wyjątek poprawnie lub zatrzymaj wyjątek runtime, aby się wydarzył. Cały punkt tego opisu to PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-13 23:23:44
Ustawienie specjalnej ramki stosu dla try / catch dodaje dodatkowe koszty, ale JVM może być w stanie wykryć fakt powrotu i zoptymalizować to.
W zależności od liczby iteracji, różnica w wydajności będzie prawdopodobnie znikoma.
Jednak zgadzam się z innymi, że posiadanie go poza pętlą sprawia, że ciało pętli wygląda czystiej.
Jeśli istnieje szansa, że będziesz chciał kontynuować przetwarzanie, a nie zakończyć, jeśli istnieje nieprawidłowy numer, wtedy chcesz, aby Kod był wewnątrz pętli.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 20:01:47
Jeśli jest w środku, zyskasz narzut struktury try/catch N razy, w przeciwieństwie do tylko raz na zewnątrz.
Za każdym razem, gdy wywołana jest struktura Try/Catch, dodaje ona narzut do wykonania metody. Wystarczy odrobina pamięci i procesora, aby poradzić sobie ze strukturą. Jeśli uruchamiasz pętlę 100 razy, a dla hipotetycznego dobra, powiedzmy, że koszt to 1 tick za połączenie try/catch, to posiadanie Try / Catch wewnątrz pętli kosztuje 100 kleszcze, w przeciwieństwie do tylko 1 kleszcza, jeśli jest poza pętlą.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 20:19:58
Cały punkt WYJĄTKÓW ma na celu zachęcenie do pierwszego stylu: umożliwienie skonsolidowania i obsługi błędów raz, a nie natychmiast na każdym możliwym miejscu błędu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 23:11:28
Włóż to do środka. Możesz kontynuować przetwarzanie (jeśli chcesz) lub możesz rzucić pomocny wyjątek, który informuje Klienta o wartości myString i indeksie tablicy zawierającej złą wartość. Myślę, że NumberFormatException już powie ci złą wartość, ale zasadą jest umieszczenie wszystkich przydatnych danych w wyjątkach, które rzucasz. Zastanów się, co byłoby dla Ciebie interesujące w debuggerze w tym momencie programu.
Rozważmy:
try {
// parse
} catch (NumberFormatException nfe){
throw new RuntimeException("Could not parse as a Float: [" + myString +
"] found at index: " + i, nfe);
}
W czasie potrzebujesz naprawdę docenią wyjątek jak ten z jak najwięcej informacji w nim, jak to możliwe.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-01 05:23:00
Chciałbym dodać własne 0.02c
o dwóch konkurencyjnych rozważaniach, patrząc na ogólny problem, gdzie umieścić obsługę wyjątków:
"szersza" odpowiedzialność
try-catch
bloku (tj. poza pętlą w Twoim przypadku) oznacza, że podczas zmiany kodu w późniejszym czasie możesz omyłkowo dodać linię, którą obsługuje twój istniejącycatch
blok; być może nieumyślnie. W Twoim przypadku jest to mniej prawdopodobne, ponieważ wyraźnie łapieszNumberFormatException
Im "węższa" odpowiedzialność bloku
try-catch
, tym trudniejsza staje się refaktoryzacja. Szczególnie, gdy (jak w Twoim przypadku) wykonujesz" nielokalną " instrukcję z blokucatch
(Instrukcjareturn null
).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-05 15:41:53
To zależy od obsługi awarii. Jeśli chcesz tylko pominąć elementy błędu, spróbuj wewnątrz:
for(int i = 0; i < max; i++) {
String myString = ...;
try {
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
} catch (NumberFormatException ex) {
--i;
}
}
W każdym innym przypadku wolałbym spróbować Na Zewnątrz. Kod jest bardziej czytelny, jest bardziej czysty. Być może lepiej byłoby rzucić IllegalArgumentException w przypadku błędu zamiast zwracać null.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-07 08:13:14
Włożę swoje 0,02 dolara. Czasami trzeba dodać" w końcu " później w kodzie (bo kto pisze swój kod idealnie za pierwszym razem?). W takich przypadkach nagle bardziej sensowne jest mieć try / catch poza pętlą. Na przykład:
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.
}
Ponieważ Jeśli pojawi się błąd, lub nie, chcesz tylko zwolnić połączenie z bazą danych (lub wybrać swój ulubiony typ innego zasobu...) raz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-09 16:30:41
Innym aspektem nie wspomnianym powyżej jest fakt, że każdy try-catch ma jakiś Wpływ na stos, co może mieć implikacje dla metod rekurencyjnych.
Jeśli metoda" outer () "wywołuje metodę" inner () "(która może wywoływać się rekurencyjnie), spróbuj zlokalizować try-catch w metodzie" outer ()", jeśli to możliwe. Prosty przykład "stack crash", którego używamy w klasie wydajności zawodzi przy około 6400 klatkach, gdy try-catch jest w metodzie wewnętrznej, i przy około 11600, gdy jest w metoda zewnętrzna.
W świecie rzeczywistym może to być problem, jeśli używasz wzorca złożonego i masz duże, złożone struktury zagnieżdżone.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-11 19:06:16
Jeśli chcesz wyłapać wyjątki dla każdej iteracji lub sprawdzić, w jakim wyjątku iteracji jest wyrzucany i wyłapać wszystkie wyjątki w iteracji, umieść try...Złap w pętli. Nie spowoduje to przerwania pętli, jeśli wystąpi wyjątek i możesz przechwycić każdy wyjątek w każdej iteracji w całej pętli.
Jeśli chcesz przerwać pętlę i sprawdzić wyjątek po wyrzuceniu, użyj try...Złap z pętli. Spowoduje to przerwanie pętli i wykonanie instrukcji po catch (jeśli any).
Wszystko zależy od twoich potrzeb. Wolę spróbować...przechwyć wewnątrz pętli podczas wdrażania, ponieważ jeśli wystąpi wyjątek, wyniki nie są niejednoznaczne i pętla nie pęknie i nie zostanie wykonana całkowicie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-23 14:59:58