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

我遇到了这样一种情况: 嵌套在两个 for循环中的 return语句总是能够到达,理论上是这样的。

编译器不同意,需要在 for循环之外使用 return语句。我想知道一种优雅的方法来优化这个方法,这种方法超出了我目前的理解范围,而且我尝试的 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
}
13690 次浏览

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

Use a temp variable, for instance "result" , and remove the inner return. 用适当的条件更改 while 循环的 for 循环。 对我来说,只有一个 return 作为函数的最后一个语句总是比较优雅的。

因为您询问了如何打破两个 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;
}

作为 @ 鲍里斯指出,您可以确保第二个 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,您将知道实现在语义上是错误的,而不必显式抛出任何东西。

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

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


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

通过这种方式,您不需要引入永远不会到达的代码。

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. 创建一个整数数组0. . range-1。将所有值设置为0。
  2. 循环播放。在循环中,生成一个随机数。看看你的列表,在那个索引,看看值是否是1,如果是,打破循环。否则,将该索引处的值设置为1
  3. 计算列表中的1的个数,并返回该值。

虽然断言是一个很好的快速解决方案。通常,这类问题意味着代码过于复杂。当我查看您的代码时,很明显您并不希望数组保存以前的数字。你想要 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();

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

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

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