Eu tenho um loop que se parece com isso:
for (int i = 0; i < max; i++) {
String myString = ...;
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
}
Esse é o conteúdo principal de um método cujo único objetivo é retornar a matriz de flutuadores. Quero que esse método retorne null
se houver um erro, então coloquei o loop dentro de um try...catch
bloco, assim:
try {
for (int i = 0; i < max; i++) {
String myString = ...;
float myNum = Float.parseFloat(myString);
myFloats[i] = myNum;
}
} catch (NumberFormatException ex) {
return null;
}
Mas também pensei em colocar o try...catch
bloco dentro do loop, assim:
for (int i = 0; i < max; i++) {
String myString = ...;
try {
float myNum = Float.parseFloat(myString);
} catch (NumberFormatException ex) {
return null;
}
myFloats[i] = myNum;
}
Existe algum motivo, desempenho ou não, para preferir um ao outro?
Edit: O consenso parece ser que é mais limpo colocar o loop dentro do try / catch, possivelmente dentro de seu próprio método. No entanto, ainda há um debate sobre o que é mais rápido. Alguém pode testar isso e voltar com uma resposta unificada?
java
performance
loops
try-catch
Michael Myers
fonte
fonte
Respostas:
DESEMPENHO:
Não há absolutamente nenhuma diferença de desempenho no local em que as estruturas try / catch são colocadas. Internamente, eles são implementados como uma tabela de intervalo de código em uma estrutura criada quando o método é chamado. Enquanto o método está em execução, as estruturas try / catch ficam completamente fora de cena, a menos que ocorra um lançamento, o local do erro é comparado com a tabela.
Aqui está uma referência: http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html
A tabela é descrita na metade do caminho.
fonte
Desempenho : como Jeffrey disse em sua resposta, em Java não faz muita diferença.
Geralmente , para facilitar a leitura do código, sua escolha de onde capturar a exceção depende se você deseja que o loop continue processando ou não.
No seu exemplo, você retornou ao capturar uma exceção. Nesse caso, eu colocaria o try / catch em volta do loop. Se você simplesmente deseja pegar um valor ruim, mas continuar o processamento, coloque-o dentro.
A terceira maneira : você sempre pode escrever seu próprio método estático ParseFloat e ter o tratamento de exceções tratado nesse método, e não no seu loop. Tornando a manipulação de exceção isolada para o próprio loop!
fonte
Tudo bem, depois que Jeffrey L Whitledge disse que não havia diferença de desempenho (em 1997), eu fui e testei. Eu executei este pequeno benchmark:
Eu verifiquei o bytecode resultante usando javap para garantir que nada fique embutido.
Os resultados mostraram que, assumindo otimizações insignificantes no JIT, Jeffrey está correto ; não há absolutamente nenhuma diferença de desempenho no Java 6, VM cliente Sun (não tive acesso a outras versões). A diferença de tempo total é da ordem de alguns milissegundos durante todo o teste.
Portanto, a única consideração é o que parece mais limpo. Acho que a segunda maneira é feia, então vou me ater à primeira ou à maneira de Ray Hayes .
fonte
Embora o desempenho possa ser o mesmo e o que "pareça" melhor seja muito subjetivo, ainda há uma grande diferença na funcionalidade. Veja o seguinte exemplo:
O loop while está dentro do bloco try catch, a variável 'j' é incrementada até atingir 40, impressa quando j mod 4 é zero e uma exceção é lançada quando j atinge 20.
Antes de qualquer detalhe, aqui está o outro exemplo:
A mesma lógica acima, a única diferença é que o bloco try / catch está agora dentro do loop while.
Aí vem a saída (enquanto em try / catch):
E a outra saída (try / catch in while):
Lá você tem uma diferença bastante significativa:
enquanto em try / catch sai do loop
try / catch enquanto mantém o loop ativo
fonte
try{} catch() {}
o loop se o loop for tratado como uma única "unidade" e encontrar um problema nessa unidade faz com que toda a "unidade" (ou loop neste caso) vá para o sul. Coloquetry{} catch() {}
dentro do loop se estiver tratando uma única iteração do loop ou um único elemento em uma matriz como uma "unidade" inteira. Se uma exceção nessa "unidade" ocorrer, simplesmente pularemos esse elemento e continuaremos para a próxima iteração do loop.Concordo com todas as postagens de desempenho e legibilidade. No entanto, existem casos em que isso realmente importa. Algumas outras pessoas mencionaram isso, mas pode ser mais fácil ver com exemplos.
Considere este exemplo ligeiramente modificado:
Se você deseja que o método parseAll () retorne nulo se houver algum erro (como o exemplo original), coloque o try / catch do lado de fora da seguinte maneira:
Na realidade, você provavelmente deve retornar um erro aqui em vez de nulo, e geralmente não gosto de ter vários retornos, mas você entendeu.
Por outro lado, se você quiser que ele ignore os problemas e analise o que for possível, poderá colocar o try / catch dentro do loop da seguinte maneira:
fonte
Como já mencionado, o desempenho é o mesmo. No entanto, a experiência do usuário não é necessariamente idêntica. No primeiro caso, você falhará rapidamente (ou seja, após o primeiro erro); no entanto, se você colocar o bloco try / catch dentro do loop, poderá capturar todos os erros que seriam criados para uma determinada chamada ao método. Ao analisar uma matriz de valores de cadeias em que você espera alguns erros de formatação, há definitivamente casos em que você gostaria de apresentar todos os erros ao usuário para que eles não precisem tentar corrigi-los um a um .
fonte
Se falhar tudo ou nada, o primeiro formato faz sentido. Se você quiser processar / retornar todos os elementos que não falham, precisará usar o segundo formulário. Esses seriam meus critérios básicos para escolher entre os métodos. Pessoalmente, se for tudo ou nada, eu não usaria o segundo formulário.
fonte
Contanto que você esteja ciente do que precisa realizar no loop, poderá tentar capturar fora do loop. Mas é importante entender que o loop terminará assim que a exceção ocorrer e que nem sempre é o que você deseja. Este é realmente um erro muito comum em software baseado em Java. As pessoas precisam processar vários itens, como esvaziar uma fila, e confiar falsamente em uma instrução try / catch externa que lida com todas as exceções possíveis. Eles também podem manipular apenas uma exceção específica dentro do loop e não esperam que ocorra qualquer outra exceção. Então, se ocorrer uma exceção que não é tratada dentro do loop, o loop será "preemted", finalizando possivelmente prematuramente e a instrução catch externa manipulará a exceção.
Se o loop tivesse como função na vida esvaziar uma fila, é provável que ele terminasse antes que a fila fosse realmente esvaziada. Falha muito comum.
fonte
Nos seus exemplos, não há diferença funcional. Acho o seu primeiro exemplo mais legível.
fonte
Você deve preferir a versão externa à versão interna. Esta é apenas uma versão específica da regra, mova qualquer coisa fora do loop que você possa mover para fora do loop. Dependendo do compilador IL e JIT, suas duas versões podem ou não ter características de desempenho diferentes.
Em outra nota, você provavelmente deve examinar float.TryParse ou Convert.ToFloat.
fonte
Se você colocar o try / catch dentro do loop, continuará fazendo o loop após uma exceção. Se você colocá-lo fora do loop, parará assim que uma exceção for lançada.
fonte
Minha perspectiva seria que os blocos try / catch são necessários para garantir o tratamento adequado das exceções, mas a criação desses blocos tem implicações no desempenho. Como os loops contêm cálculos repetitivos intensivos, não é recomendável colocar blocos try / catch dentro dos loops. Além disso, parece que onde essa condição ocorre, geralmente é "Exception" ou "RuntimeException" capturada. RuntimeException capturada no código deve ser evitada. Novamente, se você trabalha em uma grande empresa, é essencial registrar essa exceção corretamente ou impedir que a exceção de tempo de execução aconteça. O ponto inteiro desta descrição é
PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS
fonte
a configuração de um quadro de pilha especial para try / catch adiciona uma sobrecarga adicional, mas a JVM pode detectar o fato de que você está retornando e otimizar isso.
dependendo do número de iterações, a diferença de desempenho provavelmente será insignificante.
No entanto, eu concordo com os outros que tê-lo fora do loop faz com que o corpo do loop pareça mais limpo.
Se houver uma chance de que você deseje continuar com o processamento, em vez de sair se houver um número inválido, convém que o código esteja dentro do loop.
fonte
Se estiver dentro, você ganhará a sobrecarga da estrutura try / catch N vezes, em vez de apenas uma vez do lado de fora.
Toda vez que uma estrutura Try / Catch é chamada, ela adiciona sobrecarga à execução do método. Apenas um pouco da memória e do processador necessários para lidar com a estrutura. Se você estiver executando um loop 100 vezes, e por uma questão hipotética, digamos que o custo seja de 1 tick por chamada try / catch, então ter o Try / Catch dentro do loop custa 100 ticks, em oposição a apenas 1 tick se for fora do loop.
fonte
O ponto principal das exceções é incentivar o primeiro estilo: permitir que o tratamento de erros seja consolidado e tratado uma vez, e não imediatamente em todos os locais de erro possíveis.
fonte
coloque isso dentro. Você pode continuar processando (se desejar) ou pode lançar uma exceção útil que informa ao cliente o valor de myString e o índice da matriz que contém o valor inválido. Eu acho que NumberFormatException já vai lhe dizer o valor ruim, mas o princípio é colocar todos os dados úteis nas exceções que você lança. Pense no que seria interessante para você no depurador neste momento do programa.
Considerar:
Na hora da necessidade, você realmente apreciará uma exceção como essa, com o máximo de informações possível.
fonte
Gostaria de acrescentar minhas
0.02c
duas considerações concorrentes ao analisar o problema geral de onde posicionar o tratamento de exceções:A responsabilidade "mais ampla" do
try-catch
bloco (ou seja, fora do loop no seu caso) significa que, ao alterar o código em algum momento posterior, você pode adicionar por engano uma linha que é tratada pelocatch
bloco existente ; possivelmente sem querer. No seu caso, isso é menos provável porque você está capturando explicitamente umNumberFormatException
Quanto mais "restrita" a responsabilidade do
try-catch
bloco, mais difícil se torna a refatoração. Particularmente quando (como no seu caso) você está executando uma instrução "não local" de dentro docatch
bloco (areturn null
instrução).fonte
Isso depende do tratamento de falhas. Se você apenas deseja pular os elementos de erro, tente dentro de:
Em qualquer outro caso, eu preferiria tentar fora. O código é mais legível, é mais limpo. Talvez seja melhor lançar uma IllegalArgumentException no caso de erro, se retornar nulo.
fonte
Vou colocar meus US $ 0,02. Às vezes, você acaba adicionando um "finalmente" mais tarde no seu código (porque quem nunca escreveu o código perfeitamente na primeira vez?). Nesses casos, de repente faz mais sentido tentar / capturar fora do loop. Por exemplo:
Porque se você receber um erro ou não, você só deseja liberar sua conexão com o banco de dados (ou escolher seu tipo favorito de outro recurso ...) uma vez.
fonte
Outro aspecto não mencionado acima é o fato de que cada tentativa de captura tem algum impacto na pilha, o que pode ter implicações para métodos recursivos.
Se o método "outer ()" chamar o método "inner ()" (que pode se chamar recursivamente), tente localizar o try-catch no método "outer ()", se possível. Um exemplo simples de "falha na pilha" que usamos em uma classe de desempenho falha em cerca de 6.400 quadros quando o try-catch está no método interno e em cerca de 11.600 quando está no método externo.
No mundo real, isso pode ser um problema se você estiver usando o padrão Composite e tiver estruturas aninhadas grandes e complexas.
fonte
Se você deseja capturar Exception para cada iteração ou verificar em que iteração Exception é lançada e capturar todas as Exceções em uma iteração, coloque try ... catch dentro do loop. Isso não interromperá o loop se ocorrer uma exceção e você poderá capturar todas as exceções em cada iteração ao longo do loop.
Se você deseja interromper o loop e examinar a exceção sempre que lançada, use try ... catch fora do loop. Isso interromperá o loop e executará instruções após a captura (se houver).
Tudo depende da sua necessidade. Prefiro usar try ... catch dentro do loop durante a implantação, pois, se a exceção ocorrer, os resultados não serão ambíguos e o loop não será interrompido e executado completamente.
fonte