如何避免Java方法中的无用返回

时间:2021-02-18 17:20:59

I have a situation where the return statement nested in two for loops will always be reached, theoretically. The compiler disagrees and requires a return statement outside of the for loop. I'd like to know an elegant way to optimize this method that's beyond my current understanding, none of my attempted implementations of break seem to work. Attached is a method from an assignment that generates random integers and returns the iterations cycled through until a second random integer is found, generated within a range passed into the method as an int parameter.

我有这样一种情况:返回语句嵌套在两个for循环中,理论上总是可以到达。编译器不同意,它需要在for循环之外有一个返回语句。我想知道一种优雅的方法来优化这个方法,这种方法超出了我目前的理解,我尝试的break实现似乎都不起作用。附件是赋值的一个方法,该赋值生成随机整数,并返回循环遍历的迭代,直到找到第二个随机整数,并在作为int参数传入方法的范围内生成第二个随机整数。

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
}

10 个解决方案

#1


341  

The compiler's heuristics will never let you omit the last return. If you're sure it'll never be reached, I'd replace it with a throw to make the situation clear.

编译器的启发式永远不会让您忽略最后的返回。如果你确定永远也够不到的话,我就把它换成扔一扔,把情况弄清楚。

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

#2


35  

As @BoristheSpider pointed out you can make sure the second return statement is semantically unreachable:

正如@BoristheSpider指出的那样,您可以确保第二个返回语句在语义上是不可访问的:

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

Compiles & runs fine. And if you ever get an ArrayIndexOutOfBoundsException you'll know the implementation was semantically wrong, without having to explicitly throw anything.

编译和运行的很好。如果你得到一个ArrayIndexOutOfBoundsException,你就会知道这个实现在语义上是错误的,而不需要显式抛出任何东西。

#3


17  

Since you asked about breaking out of two for loops, you can use a label to do that (see the example below):

既然您已经询问了如何从两个for循环中跳出来,您可以使用一个标签来完成这个任务(请参见下面的示例):

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

#4


12  

While an assert is a good fast solution. In general this kind of problems means that your code is too complicated. When I am looking at your code, it's obvious that you don't really want an array to hold previous numbers. You want a 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();

Now note that what we are returning is actually the size of the set. The code complexity has decreased from quadratic to linear and it is immediately more readable.

现在要注意的是,我们返回的是集合的大小。代码的复杂性已经从二次线性减少到线性,并且它马上就变得更加可读了。

Now we can realize that we don't even need that 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();

#5


7  

As your return value is based on the outer loop's variable you could simply alter the outer loop's condition to count < range and then return this last value (which you've just omitted) at the end of the function:

由于您的返回值是基于外部循环的变量的,您可以简单地更改外部循环的条件,以计数< range,然后在函数末尾返回最后的值(您刚刚省略了):

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

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

This way you don't need to introduce code that will never be reached.

这样,您就不需要引入永远无法到达的代码。

#6


5  

Use a temp variable, for instance "result" , and remove the inner return. Change the for loop for a while loop with the proper condition. To me it's always more elegant to have only one return as the last statement of the function.

使用一个临时变量,例如“result”,并删除内部返回。用适当的条件将for循环更改为一个while循环。对于我来说,只有一个返回作为函数的最后一个语句总是比较优雅的。

#7


3  

Maybe this is an indication that you should rewrite your code. For example:

也许这表明您应该重写代码。例如:

  1. Create an array of integers 0 .. range-1. Set all the values to 0.
  2. 创建一个整数数组。范围1。将所有值设置为0。
  3. Perform a loop. In the loop, generate a random number. Look in your list, at that index, to see if the value is 1 If it is, break out of the loop. Otherwise, set the value at that index to 1
  4. 执行一个循环。在循环中,生成一个随机数。看看你的列表,看看索引,如果值是1,就跳出循环。否则,将该索引值设置为1。
  5. Count the number of 1s in the list, and return that value.
  6. 数一下列表中的1的个数,然后返回那个值。

#8


3  

Methods that have a return statement and have a loop/loops inside them always require a return statement outside the loop(s). Even if this statement outside the loop is never reached. In such cases, in order to avoid unnecessary return statements, you could define a variable of the respective type, an integer in your case, at the beginning of the method i.e. before and outside the respective loop(s). When the desired result inside the loop is reached, you can ascribe the respective value to this pre-defined variable and use it for the return statement outside the loop.

具有返回语句且内部有循环/循环的方法总是需要循环之外的返回语句。即使这个语句在循环之外永远不会到达。在这种情况下,为了避免不必要的返回语句,您可以在方法的开头(即在各自的循环前后)定义相应类型的变量,在您的例子中定义一个整数。当达到循环中所需的结果时,可以将相应的值赋给这个预定义的变量,并将其用于循环外的返回语句。

Since you want your method to return the first result when rInt[i] equals rInt[count], implementing only the above-mentioned variable is not enough because the method will return the last result when rInt[i] equals rInt[count]. One options is to implement two "break statements" that are called when the we have the desired result. So, the method will look something like this:

由于您希望您的方法在rInt[i]等于rInt[count]时返回第一个结果,因此仅实现上述变量是不够的,因为当rInt[i]等于rInt[count]时,该方法将返回最后一个结果。一种选择是实现两个“break语句”,当我们获得预期结果时调用它们。方法是这样的

private static int oneRun(int range) {

        int finalResult = 0; // the above-mentioned variable
        int[] rInt = new int[range + 1];
        rInt[0] = generator.nextInt(range);

        for (int count = 1; count <= range; count++) {
            rInt[count] = generator.nextInt(range);
            for (int i = 0; i < count; i++) {
                if (rInt[i] == rInt[count]) {
                    finalResult = count;
                    break; // this breaks the inside loop
                }
            }
            if (finalResult == count) {
                break; // this breaks the outside loop
            }
        }
        return finalResult;
    }

#9


2  

I agree that one should throw an exception where unreachable statement occurs. Just wanted to show how the same method can do this in more readable way (java 8 streams required).

我同意应该在出现不可达语句时抛出异常。只是想展示相同的方法如何以更可读的方式来实现这一点(需要java 8流)。

private static int oneRun(int range) {
    int[] rInt = new int[range + 1];
    return IntStream
        .rangeClosed(0, range)
        .peek(i -> rInt[i] = generator.nextInt(range))
        .filter(i -> IntStream.range(0, i).anyMatch(j -> rInt[i] == rInt[j]))
        .findFirst()
        .orElseThrow(() -> new RuntimeException("Shouldn't be reached!"));
}

#10


-1  

private static int oneRun(int range) {
    int result = -1; // use this to store your result
    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 && result == -1; count++) { // Run until result found.
        rInt[count] = generator.nextInt(range); // Add randint to current iteration.   
        for (int i = 0; i < count && result == -1; i++) { // Check for past occurence and leave after result found.
            if (rInt[i] == rInt[count]) {
                result = count;
            }
        }
    }
    return result; // return your result
}

#1


341  

The compiler's heuristics will never let you omit the last return. If you're sure it'll never be reached, I'd replace it with a throw to make the situation clear.

编译器的启发式永远不会让您忽略最后的返回。如果你确定永远也够不到的话,我就把它换成扔一扔,把情况弄清楚。

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

#2


35  

As @BoristheSpider pointed out you can make sure the second return statement is semantically unreachable:

正如@BoristheSpider指出的那样,您可以确保第二个返回语句在语义上是不可访问的:

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

Compiles & runs fine. And if you ever get an ArrayIndexOutOfBoundsException you'll know the implementation was semantically wrong, without having to explicitly throw anything.

编译和运行的很好。如果你得到一个ArrayIndexOutOfBoundsException,你就会知道这个实现在语义上是错误的,而不需要显式抛出任何东西。

#3


17  

Since you asked about breaking out of two for loops, you can use a label to do that (see the example below):

既然您已经询问了如何从两个for循环中跳出来,您可以使用一个标签来完成这个任务(请参见下面的示例):

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

#4


12  

While an assert is a good fast solution. In general this kind of problems means that your code is too complicated. When I am looking at your code, it's obvious that you don't really want an array to hold previous numbers. You want a 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();

Now note that what we are returning is actually the size of the set. The code complexity has decreased from quadratic to linear and it is immediately more readable.

现在要注意的是,我们返回的是集合的大小。代码的复杂性已经从二次线性减少到线性,并且它马上就变得更加可读了。

Now we can realize that we don't even need that 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();

#5


7  

As your return value is based on the outer loop's variable you could simply alter the outer loop's condition to count < range and then return this last value (which you've just omitted) at the end of the function:

由于您的返回值是基于外部循环的变量的,您可以简单地更改外部循环的条件,以计数< range,然后在函数末尾返回最后的值(您刚刚省略了):

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

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

This way you don't need to introduce code that will never be reached.

这样,您就不需要引入永远无法到达的代码。

#6


5  

Use a temp variable, for instance "result" , and remove the inner return. Change the for loop for a while loop with the proper condition. To me it's always more elegant to have only one return as the last statement of the function.

使用一个临时变量,例如“result”,并删除内部返回。用适当的条件将for循环更改为一个while循环。对于我来说,只有一个返回作为函数的最后一个语句总是比较优雅的。

#7


3  

Maybe this is an indication that you should rewrite your code. For example:

也许这表明您应该重写代码。例如:

  1. Create an array of integers 0 .. range-1. Set all the values to 0.
  2. 创建一个整数数组。范围1。将所有值设置为0。
  3. Perform a loop. In the loop, generate a random number. Look in your list, at that index, to see if the value is 1 If it is, break out of the loop. Otherwise, set the value at that index to 1
  4. 执行一个循环。在循环中,生成一个随机数。看看你的列表,看看索引,如果值是1,就跳出循环。否则,将该索引值设置为1。
  5. Count the number of 1s in the list, and return that value.
  6. 数一下列表中的1的个数,然后返回那个值。

#8


3  

Methods that have a return statement and have a loop/loops inside them always require a return statement outside the loop(s). Even if this statement outside the loop is never reached. In such cases, in order to avoid unnecessary return statements, you could define a variable of the respective type, an integer in your case, at the beginning of the method i.e. before and outside the respective loop(s). When the desired result inside the loop is reached, you can ascribe the respective value to this pre-defined variable and use it for the return statement outside the loop.

具有返回语句且内部有循环/循环的方法总是需要循环之外的返回语句。即使这个语句在循环之外永远不会到达。在这种情况下,为了避免不必要的返回语句,您可以在方法的开头(即在各自的循环前后)定义相应类型的变量,在您的例子中定义一个整数。当达到循环中所需的结果时,可以将相应的值赋给这个预定义的变量,并将其用于循环外的返回语句。

Since you want your method to return the first result when rInt[i] equals rInt[count], implementing only the above-mentioned variable is not enough because the method will return the last result when rInt[i] equals rInt[count]. One options is to implement two "break statements" that are called when the we have the desired result. So, the method will look something like this:

由于您希望您的方法在rInt[i]等于rInt[count]时返回第一个结果,因此仅实现上述变量是不够的,因为当rInt[i]等于rInt[count]时,该方法将返回最后一个结果。一种选择是实现两个“break语句”,当我们获得预期结果时调用它们。方法是这样的

private static int oneRun(int range) {

        int finalResult = 0; // the above-mentioned variable
        int[] rInt = new int[range + 1];
        rInt[0] = generator.nextInt(range);

        for (int count = 1; count <= range; count++) {
            rInt[count] = generator.nextInt(range);
            for (int i = 0; i < count; i++) {
                if (rInt[i] == rInt[count]) {
                    finalResult = count;
                    break; // this breaks the inside loop
                }
            }
            if (finalResult == count) {
                break; // this breaks the outside loop
            }
        }
        return finalResult;
    }

#9


2  

I agree that one should throw an exception where unreachable statement occurs. Just wanted to show how the same method can do this in more readable way (java 8 streams required).

我同意应该在出现不可达语句时抛出异常。只是想展示相同的方法如何以更可读的方式来实现这一点(需要java 8流)。

private static int oneRun(int range) {
    int[] rInt = new int[range + 1];
    return IntStream
        .rangeClosed(0, range)
        .peek(i -> rInt[i] = generator.nextInt(range))
        .filter(i -> IntStream.range(0, i).anyMatch(j -> rInt[i] == rInt[j]))
        .findFirst()
        .orElseThrow(() -> new RuntimeException("Shouldn't be reached!"));
}

#10


-1  

private static int oneRun(int range) {
    int result = -1; // use this to store your result
    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 && result == -1; count++) { // Run until result found.
        rInt[count] = generator.nextInt(range); // Add randint to current iteration.   
        for (int i = 0; i < count && result == -1; i++) { // Check for past occurence and leave after result found.
            if (rInt[i] == rInt[count]) {
                result = count;
            }
        }
    }
    return result; // return your result
}