212 votes

Devriez essayer... catch aller à l’intérieur ou à l’extérieur une boucle ?

J'ai une boucle qui ressemble à quelque chose comme ceci:

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

C'est le contenu principal d'une méthode dont le seul but est de retourner un tableau de chars. Je veux cette méthode pour revenir en null si il y a une erreur, alors j'ai mis la boucle à l'intérieur d'un try...catch bloc, comme ceci:

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

Mais j'ai aussi pensé à mettre la try...catch bloc à l'intérieur de la boucle, comme ceci:

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

Est-il une raison, de la performance ou autrement, de préférer l'un sur l'autre?


Edit: Le consensus semble être que c'est plus propre de mettre la boucle à l'intérieur de la try/catch, éventuellement à l'intérieur de sa propre méthode. Cependant, il y a toujours un débat sur qui est plus rapide. Quelqu'un peut essayer et revenir avec une réponse unifiée?

148voto

Jeffrey L Whitledge Points 27574

PERFORMANCE:

Il n'y a absolument aucune différence de performances lorsque le try/catch structures sont placées. En interne, ils sont mis en œuvre comme un code-fourchette de table dans une structure qui est créé lorsque la méthode est appelée. Bien que la méthode est en cours d'exécution, le try/catch structures sont complètement hors de la photo, à moins qu'un jet de produit, alors l'emplacement de l'erreur est comparée à la table.

Voici une référence: http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html

Le tableau est décrit à mi-chemin vers le bas.

81voto

Ray Hayes Points 9819

Performance: comme Jeffrey a dit dans sa réponse, en Java il n'a pas beaucoup de différence.

Généralement, pour des raisons de lisibilité du code, votre choix de l'endroit pour attraper l'exception dépend si vous voulez que la boucle afin que le traitement ou pas.

Dans votre exemple, vous retourné lors de l'interception d'une exception. Dans ce cas, je mettrais le try/catch autour de la boucle. Tout simplement si vous voulez attraper une mauvaise valeur, mais de poursuivre le traitement, la mettre à l'intérieur.

La troisième voie: Vous pouvez toujours écrire votre propre statique ParseFloat méthode et ont la gestion des exceptions est traité dans cette méthode plutôt que de votre boucle. Faire la gestion des exceptions isolées dans la boucle elle-même!

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

49voto

Michael Myers Points 82361

Tout droit, après Jeffrey L Whitledge dit qu'il n'y a pas de différence de performances (en 1997), je suis allé et l'a testé. J'ai couru ce petit test:

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

J'ai vérifié l'résultant de pseudo-code binaire à l'aide de javap pour s'assurer que rien n'a été insérée.

Les résultats ont montré que, en supposant négligeable optimisations JIT, Jeffrey est correct; il n'y a absolument aucune différence de performances sur la version 6 de Java, Sun client VM (je n'ai pas accès à d'autres versions). Le total de la différence de temps est de l'ordre de quelques millisecondes sur l'ensemble de test.

Par conséquent, la seule contrepartie est ce qui a l'air plus propre. Je trouve que la deuxième façon est moche, donc je m'en tiendrai à la première manière ou Ray Hayes façon.

14voto

Matt N Points 578

Je suis d'accord avec toutes les performances et la lisibilité des messages. Cependant, il y a des cas où il n'a vraiment d'importance. Un couple d'autres personnes mentionnées, mais il pourrait être plus facile à voir avec des exemples.

Considérez ceci légèrement modifiée, par exemple:

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

Si vous voulez le parseAll() permet de retourner la valeur null si il y a des erreurs (comme l'original exemple), vous auriez mis le try/catch sur l'extérieur comme ceci:

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

En réalité, vous devriez probablement retourner une erreur ici, au lieu de null, et généralement, je n'aime pas avoir plusieurs retours, mais vous obtenez l'idée.

D'autre part, si vous voulez qu'il suffit d'ignorer les problèmes, et d'analyser ce que les Chaînes c'est possible, vous auriez mis le try/catch à l'intérieur de la boucle comme ceci:

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

5voto

user19810 Points 41

Comme déjà mentionné, la performance est la même. Cependant, l'expérience utilisateur n'est pas nécessairement identiques. Dans le premier cas, vous allez échouer rapidement (c'est à dire après la première erreur), mais si vous mettez le bloc try/catch à l'intérieur de la boucle, vous pouvez capturer toutes les erreurs qui auraient pu être créés pour un appel à la méthode. Lors de l'analyse d'un tableau de valeurs à partir de chaînes où vous attendent quelques erreurs de mise en forme, il y a certainement des cas où vous aimeriez être en mesure de présenter toutes les erreurs de l'utilisateur, de sorte qu'ils n'ont pas besoin d'essayer de les résoudre un par un.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X