如何避免Java方法中的无用返回
理论上来说,我总是会遇到两个for循环嵌套的return语句。 编译器不同意并且需要在for循环之外的return语句。 我想知道一个优雅的方法来优化这个方法,这超出了我目前的理解范围,但是我没有尝试过中断的实现。 Attached是一个从赋值中产生随机整数的方法,并返回循环的迭代,直到find第二个随机整数,并在作为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 }
编译器的启发式决不会让你忽略最后的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"); }
正如@BoristheSpider指出,你可以确保第二个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
你就会知道这个实现在语义上是错误的,而不必显式地抛出任何东西。
既然你问了两个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; }
而断言是一个很好的快速解决scheme。 通常这种问题意味着你的代码太复杂了。 当我看着你的代码时,很明显你并不需要一个数组来保存以前的数字。 你想要一个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();
由于你的返回值是基于外层循环的variables,所以你可以简单地改变外层循环的条件来count < range
,然后在函数结尾返回最后一个值(你刚刚省略):
private static int oneRun(int range) { ... for (int count = 1; count < range; count++) { ... } return range; }
这样你就不需要引入永远不会到达的代码。
使用一个临时variables,例如“结果”,并删除内部返回。 用合适的条件改变for循环。 对我来说,只有一个返回作为函数的最后一个语句总是更优雅。
也许这是一个迹象,你应该重写你的代码。 例如:
- 创build一个整数数组0 .. range-1。 将所有值设置为0。
- 执行一个循环。 在循环中,生成一个随机数。 看看你的清单,在该索引,看看是否是1如果是,跳出循环。 否则,将该索引处的值设置为1
- 计算列表中的1的数量,并返回该值。
有一个返回语句并且在其中有一个循环/循环的方法总是需要循环外部的一个返回语句。 即使这个循环之外的声明永远不会达到。 在这种情况下,为了避免不必要的返回语句,你可以在方法的开始部分,即在相应的循环之前和之外,定义一个相应types的variables,在你的情况下是一个整数。 当达到循环内部的所需结果时,可以将相应的值归于此预定义variables,并将其用于循环外部的返回语句。
既然你想让你的方法在rInt [i]等于rInt [count]时返回第一个结果,那么只实现上面提到的variables是不够的,因为当rInt [i]等于rInt [count]时,方法将返回最后的结果。 一个select是实现两个“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; }
我同意应该在发生无法访问的语句时抛出一个exception。 只是想展示如何用同样的方法可读性更好(需要java 8stream)。
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!")); }
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 }