116 votes

Comment éviter un retour inutile dans une méthode Java?

J'ai une situation où l' return déclaration imbriquée dans les deux for boucles sera toujours atteint, en théorie.

Le compilateur n'est pas d'accord et nécessite un return déclaration à l'extérieur de l' for boucle. Je voudrais savoir d'une manière élégante d'optimiser cette méthode qui est au-delà de ma compréhension actuelle, et aucune de mes tentatives de mises en œuvre de casser l'impression de travailler.

Ci-joint une méthode à partir d'une affectation qui génère aléatoirement des nombres entiers et renvoie les itérations défilent jusqu'à ce qu'un deuxième nombre entier aléatoire est trouvé, générés à l'intérieur d'une fourchette passé dans la méthode comme un int en paramètre.

private static int oneRun(int range) {
    int[] rInt = new int[range+1]; // Stores the past sequence of ints.
    rInt[0] = generator.nextInt(range); // Inital random number.

    for (int count = 1; count <= range; count++) { // Run until return.
        rInt[count] = generator.nextInt(range); // Add randint to current iteration.
        for (int i = 0; i < count; i++) { // Check for past occurence and return if found.
            if (rInt[i] == rInt[count]) {
                return count;
            }
        }
    }
    return 0; // Never reached
}

344voto

John Kugelman Points 108754

L'heuristique du compilateur ne vous laissera jamais omettre le dernier return . Si vous êtes sûr qu'il ne sera jamais atteint, je le remplacerais par un throw pour clarifier la situation.

 private static int oneRun(int range) {
    int[] rInt = new int[range+1]; // Stores the past sequence of ints.
    rInt[0] = generator.nextInt(range); // Inital random number.

    for (int count = 1; count <= range; count++) {
        ...
    }

    throw new AssertionError("unreachable code reached");
}
 

36voto

l0b0 Points 10719

Comme @BoristheSpider l'a souligné, vous pouvez vous assurer que la deuxième instruction return est sémantiquement inaccessible:

 private static int oneRun(int range) {
    int[] rInt = new int[range+1]; // Stores the past sequence of ints.
    int count = 0;

    while (true) {
        rInt[count] = generator.nextInt(range); // Add randint to current iteration.
        for (int i = 0; i < count; i++) { // Check for past occurence and return if found.
            if (rInt[i] == rInt[count]) {
                return count;
            }
        }
        count++;
    }
}
 

Compile et fonctionne bien. Et si vous obtenez un ArrayIndexOutOfBoundsException vous saurez que l'implémentation est sémantiquement incorrecte, sans avoir à lancer explicitement quoi que ce soit.

18voto

David Choweller Points 965

Puisque vous avez demandé si vous vouliez rompre deux boucles for , vous pouvez utiliser une étiquette pour le faire (voir l'exemple ci-dessous):

 private static int oneRun(int range) {
    int returnValue=-1;

    int[] rInt = new int[range+1]; // Stores the past sequence of ints.
    rInt[0] = generator.nextInt(range); // Inital random number.

    OUTER: for (int count = 1; count <= range; count++) { // Run until return.
        rInt[count] = generator.nextInt(range); // Add randint to current iteration.   
        for (int i = 0; i < count; i++) { // Check for past occurence and return if found.
            if (rInt[i] == rInt[count]) {
                returnValue = count;
                break OUTER;
            }
        }
    }
    return returnValue;
}
 

13voto

Sulthan Points 23360

Alors qu'une assertion est une bonne solution rapide. En général ce genre de problèmes signifie que votre code est trop compliqué. Quand je suis à la recherche à votre code, il est évident que vous ne voulez pas vraiment un tableau pour contenir numéros précédents. Vous voulez un Set:

Set<Integer> previous = new HashSet<Integer>();

int randomInt = generator.nextInt(range);
previous.add(randomInt);

for (int count = 1; count <= range; count++) {
    randomInt = generator.nextInt(range);
    if (previous.contains(randomInt)) {
       break;
    }

    previous.add(randomInt);
}

return previous.size();

Maintenant, notez que ce que nous sommes de retour est en fait la taille de l'ensemble. La complexité du code, a diminué de quadratique linéaire et il est immédiatement plus lisible.

Maintenant, nous pouvons nous rendre compte que nous n'avons même pas besoin qu' count index:

Set<Integer> previous = new HashSet<Integer>();

int randomInt = generator.nextInt(range);

while (!previous.contains(randomInt)) {          
    previous.add(randomInt);      
    randomInt = generator.nextInt(range);
}

return previous.size();

8voto

a_guest Points 5059

Comme votre valeur de retour est basée sur la variable de la boucle externe, vous pouvez simplement modifier la condition de la boucle externe en count < range , puis renvoyer cette dernière valeur (que vous venez d'omettre) à la fin de la fonction:

 private static int oneRun(int range) {
    ...

    for (int count = 1; count < range; count++) {
        ...
    }
    return range;
}
 

De cette façon, vous n'avez pas besoin d'introduire du code qui ne sera jamais atteint.

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