Pergunta

Eu tenho um loop que é algo como isto:

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

Este é o conteúdo principal de um método cujo único propósito é retornar a matriz de carros alegóricos. Quero que este método para retornar null se houver um erro, então eu coloquei o loop dentro de um bloco try...catch, como este:

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

Mas então eu também pensei em colocar o bloco try...catch dentro do loop, como este:

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

Existe alguma razão, o desempenho ou de outra forma, a preferir um sobre o outro?


Editar: O consenso parece ser que é mais limpo para colocar o laço dentro do try / catch, possivelmente dentro de seu próprio método. No entanto, ainda há debate sobre o que é mais rápido. alguém teste isso e pode voltar com uma resposta unificada?

Foi útil?

Solução 3

Tudo bem, depois Jeffrey L Whitledge disse que não houve diferença de desempenho (a partir de 1997), eu fui e testei. Corri esta pequena referência:

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;
        }
    }
}

Eu verifiquei o bytecode resultante usando javap para se certificar de que nada ficou embutido.

Os resultados mostraram que, assumindo otimizações insignificante JIT, Jeffrey está correto ; não há absolutamente nenhuma diferença de desempenho em Java 6, Sun cliente VM (Eu não têm acesso a outras versões). A diferença de tempo total é da ordem de alguns milissegundos ao longo de todo o teste.

Portanto, a única consideração é o que parece mais limpo. Eu acho que a segunda maneira é feio, por isso vou ficar com tanto a primeira forma ou de Ray Hayes maneira .

Outras dicas

DESEMPENHO:

Não há absolutamente nenhuma diferença de desempenho em onde as estruturas try / catch são colocados. Internamente, elas são implementadas como uma tabela de código-range em uma estrutura que é criada quando o método é chamado. Enquanto o método está em execução, o try / estruturas de captura são completamente fora de cogitação a menos que ocorra um lance, em seguida, a localização 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 a meio caminho para baixo.

Desempenho : como Jeffrey disse em sua resposta, em Java não faz muita diferença.

Geralmente , para facilitar a leitura do código, a sua escolha de onde pegar a exceção depende se você quer que o loop para manter o processamento ou não.

No seu exemplo você devolvido após a captura de uma exceção. Nesse caso, eu ia colocar o try / catch em torno do laço. Se você simplesmente quer pegar um valor ruim, mas carry on processamento, colocá-lo dentro.

A terceira maneira : Você pode sempre escrever o seu método parseFloat própria estática e ter o tratamento de exceção tratado em que o método ao invés de seu loop. Fazer a manipulação de exceção isolada ao próprio laço!

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;
    }
}

Embora o desempenho pode ser o mesmo e que "olha" melhores é muito subjetivo, ainda há uma diferença muito grande em termos de funcionalidade. Veja o seguinte exemplo:

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");
    }

O enquanto circuito está dentro do bloco de teste de captura, a variável 'j' é incrementada até que atinja 40, impresso quando j mod 4 é zero e uma exceção é lançada quando j atinge 20.

Antes de quaisquer detalhes, aqui outro exemplo:

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"); }
    }

A mesma lógica acima, a única diferença é que o bloco try / catch é agora dentro do loop while.

Aqui vem a saída (enquanto em try / catch):

4
8
12 
16
in catch block

E a outra saída (try / catch em quando):

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

Lá você tem bastante uma diferença significativa:

enquanto em try / quebras de captura fora do laço

try / catch em enquanto mantém o circuito ativo

Concordo com todos os postos de desempenho e legibilidade. No entanto, existem casos em que realmente importa. Um par de outras pessoas mencionado isso, mas pode ser mais fácil ver com exemplos.

Veja este exemplo ligeiramente modificado:

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;
}

Se você quer o método parseAll () como nulo retorno se houver algum erro (como o exemplo original), você iria colocar o try / catch no exterior como esta:

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;
}

Na realidade, você provavelmente deve retornar um erro aqui em vez de null, e geralmente eu não gosto de ter vários retornos, mas essa é a idéia.

Por outro lado, se você quer que ele simplesmente ignorar os problemas e analisar o que quer Cordas ele pode, você iria colocar o try / catch no interior do loop como este:

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;
}

Como já mencionado, o desempenho é o mesmo. No entanto, a experiência do usuário não é necessariamente idêntico. No primeiro caso, você vai falhar rápido (ou seja, após o primeiro erro), no entanto, se você colocar o bloco try / catch dentro do loop, você pode capturar todos os erros que seriam criados para uma determinada chamada para o método. Ao analisar uma matriz de valores de cordas onde você espera alguns erros de formatação, há definitivamente casos em que você gostaria de ser capaz de apresentar todos os erros ao usuário de modo que eles não precisam para tentar corrigi-los um por um .

Se seu um tudo-ou-nada falhar, então o primeiro formato faz sentido. Se você quer ser capaz de processo / retornar todos os elementos não-não, você precisa usar a segunda forma. Esses seriam os meus critérios básicos para a escolha entre os métodos. Pessoalmente, se é tudo ou nada, eu não iria usar o segundo formulário.

Enquanto você está ciente do que você precisa para realizar no circuito você pode colocar o try catch fora do loop. Mas é importante entender que o loop terminará então assim que a exceção ocorre e que pode não ser sempre o que você quer. Este é realmente um erro muito comum em software baseado em Java. As pessoas precisam processar um número de itens, tais como esvaziar uma fila, e falsamente contar com um try / catch exterior lidar com todas as exceções possíveis. Eles também poderiam ser lidar com apenas uma exceção específica dentro do loop e não esperar que qualquer outra exceção para ocorrer. Em seguida, se ocorrer uma exceção que não é tratada dentro do loop, em seguida, o loop será "preemted", termina possivelmente prematuramente e as capturas exterior declaração manipula a exceção.

Se o circuito teve como seu papel na vida de esvaziar uma fila, em seguida, esse ciclo muito provável poderia terminar antes que fila foi realmente esvaziado. falha muito comum.

Em seus exemplos, não há diferença funcional. I encontrar o seu primeiro exemplo mais legível.

Você deve preferir a versão externa sobre a versão interna. Esta é apenas uma versão específica da regra, mover qualquer coisa fora do loop que você pode mover fora do loop. Dependendo do compilador IL e JIT compilador suas duas versões podem ou não acabar com diferentes características de desempenho.

Em outra nota você provavelmente deve olhar para float.TryParse ou Convert.ToFloat.

Se você colocar o try / catch dentro do loop, você vai manter looping após uma exceção. Se você colocá-lo fora do loop você vai parar logo que uma exceção é lançada.

A minha perspectiva seria blocos try / catch são necessárias para garantir o tratamento de exceção adequada, mas a criação de tais blocos tem implicações de desempenho. Uma vez que, Loops conter cálculos repetitivos intensivos, não é recomendado para colocar blocos try / catch dentro de loops. Além disso, parece que essa condição ocorre, muitas vezes é "exceção" ou "RuntimeException", que está preso. RuntimeException ser capturada no código deve ser evitado. Novamente, se se você trabalha em uma grande empresa é essencial para registrar essa exceção corretamente, ou exceção parada tempo de execução para acontecer. ponto de toda esta descrição é PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS

criação de um quadro de pilha especial para o try / catch adiciona uma sobrecarga adicional, mas a JVM pode ser capaz de detectar o fato de que você está retornando e otimizar essa distância.

, dependendo do número de iterações, diferença de desempenho será provavelmente insignificante.

No entanto, eu concordo com os outros que tê-lo fora do loop fazer o limpador de corpo do laço look.

Se há uma chance de que você nunca vai querer continuar com o processamento, em vez de sair se houver um número inválido, então você gostaria que o código seja dentro do loop.

Se ele está dentro, então você vai ganhar a sobrecarga da estrutura try / catch N vezes, ao invés de apenas uma vez do lado de fora.


Cada vez que uma estrutura try / catch é chamada, ela adiciona uma sobrecarga para a execução do método. Apenas o pouco de memória e processador de carrapatos necessária para lidar com a estrutura. Se você estiver executando um loop de 100 vezes, e por amor hipotético, digamos que o custo é um carrapato por try / chamada captura, em seguida, tendo o try / catch dentro do loop custa 100 carrapatos, em oposição a apenas 1 carrapato se é fora do loop.

O ponto inteiro de exceções é incentivar o primeiro estilo:. Permitindo a manipulação de erro ser consolidadas e tratadas imediatamente, não imediatamente em cada local de erro possível

colocá-lo dentro. Você pode manter o processamento (se você quiser) ou você pode lançar uma exceção útil que diz ao cliente o valor de myString e o índice do array contendo o valor ruim. Acho NumberFormatException já irá dizer-lhe o valor ruim, mas o princípio é colocar todos os dados úteis para as exceções que você joga. Pense sobre o que seria interessante para você no depurador neste momento no programa.

Considere o seguinte:

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

No momento de necessidade você vai realmente apreciar uma exceção como este, com tanta informação nele possível.

I é como adicionar minha própria 0.02c sobre duas considerações concorrentes quando se olha para o problema geral de onde a manipulação de exceção posição:

  1. O "mais amplo" da responsabilidade do bloco try-catch (ou seja, fora do loop no seu caso) significa que ao mudar o código em algum momento mais tarde, você pode equivocadamente adicionar uma linha que é tratado pelo seu bloco catch existente ; possivelmente sem querer. No seu caso, isso é menos provável porque você está pegando explicitamente um NumberFormatException

  2. O "estreito" a responsabilidade do bloco try-catch, a refatoração mais difícil se torna. Especialmente quando (como no seu caso) está a executar uma instrução de "não-local" a partir de dentro do bloco catch (a declaração return null).

Isso depende do tratamento de falhas. Se você só quer ignorar os elementos de erro, tente dentro:

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

Em qualquer outro caso, eu preferiria o exterior tentativa. O código é mais legível, é mais limpo. Talvez seria melhor para lançar uma IllegalArgumentException no caso de erro em vez se retornando null.

Vou colocar meus $ 0,02. Às vezes você acabar precisando de adicionar um "finalmente", mais tarde, em seu código (porque quem já escreve seu código perfeitamente da primeira vez?). Nesses casos, de repente, faz mais sentido ter o try / catch fora do loop. Por exemplo:

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.
}

Porque se você receber um erro, ou não, você só quer liberar a conexão de banco de dados (ou escolher o seu tipo favorito de outro recurso ...) uma vez.

Outro aspecto não mencionado na acima é o fato de que cada try-catch tem alguns impacto sobre a pilha, o que pode ter implicações para métodos recursiva.

Se o método método "exteriores ()" chamadas "interior ()" (que pode se chamar de forma recursiva), tentar localizar o try-catch no método "exterior ()", se possível. Um simples "acidente pilha" exemplo que usamos em uma classe de desempenho falha em cerca de 6.400 quadros quando o try-catch está no método interior, e em cerca de 11.600, quando se está no método exterior.

No mundo real, isso pode ser um problema se você estiver usando o padrão Composite e têm, estruturas aninhadas grandes complexos.

Se você quiser Exceção de captura para cada iteração, ou cheque para o que iteração exceção é lançada e pegar cada Exceções em uma itertaion, lugar try ... catch dentro do loop. Isso não vai quebrar o ciclo se exceção ocorre e você pode pegar todos os Exceção em cada iteração ao longo do loop.

Se você quiser quebrar o ciclo e examinar a exceção sempre jogado, o uso try ... catch fora do circuito. Isto vai quebrar o ciclo e executar instruções após captura (se qualquer).

Tudo depende de sua necessidade. Eu prefiro usar try ... catch dentro do loop while implantação como, se exceção ocorre, os resultados não são ambíguos e loop não vai quebrar e executar completamente.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top