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

问题描述 投票:114回答:10

我有一种情况,理论上总是会达到嵌套在两个return循环中的for语句。

编译器不同意并且需要在return循环之外的for语句。我想知道一种优雅的方法来优化这种方法,这超出了我目前的理解,并且我尝试的中断实现似乎都不起作用。

Attached是一种来自赋值的方法,它生成随机整数并返回循环的迭代,直到找到第二个随机整数,在作为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
}
java
10个回答
344
投票

编译器的启发式方法永远不会让你省略最后的return。如果你确定它永远不会到达,我会用throw替换它以使情况清楚。

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

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

36
投票

作为@BoristheSpider pointed out,您可以确保第二个return语句在语义上无法访问:

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

编译并运行良好。如果你得到一个ArrayIndexOutOfBoundsException,你会知道实现在语义上是错误的,而不必明确地抛出任何东西。


18
投票

由于您询问了两个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;
}

13
投票

虽然断言是一个很好的快速解决方案。一般来说,这种问题意味着您的代码太复杂了。当我查看你的代码时,显然你并不真的想要一个数组来保存以前的数字。你想要一个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();

现在请注意,我们返回的内容实际上是集合的大小。代码复杂度从二次变为线性,并且立即更具可读性。

现在我们可以意识到我们甚至不需要那个count指数:

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();

8
投票

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

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

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

这样您就不需要引入永远无法访问的代码。


5
投票

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


3
投票

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

  1. 创建一个整数数组0 .. range-1。将所有值设置为0。
  2. 执行循环。在循环中,生成一个随机数。在列表中查看该索引,查看值是否为1如果是,则跳出循环。否则,将该索引处的值设置为1
  3. 计算列表中的1的数量,并返回该值。

3
投票

具有return语句并在其中包含循环/循环的方法总是需要在循环外部使用return语句。即使永远不会达到循环之外的这种说法。在这种情况下,为了避免不必要的返回语句,您可以定义相应类型的变量,在您的情况下为整数,在方法的开头,即在相应的循环之前和之外。当达到循环内的所需结果时,您可以将相应的值归因于此预定义变量,并将其用于循环外的return语句。

由于你希望你的方法在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;
    }

2
投票

我同意应该抛出无法访问语句的异常。只是想展示相同的方法如何以更易读的方式执行此操作(需要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!"));
}
© www.soinside.com 2019 - 2024. All rights reserved.