gpt4 book ai didi

java - 递归方法调用在 kotlin 中会导致 StackOverFlowError,但在 java 中不会

转载 作者:行者123 更新时间:2023-12-02 05:03:06 31 4
gpt4 key购买 nike

我在 java 和 kotlin 中有两个几乎相同的代码

Java:

public void reverseString(char[] s) {
helper(s, 0, s.length - 1);
}

public void helper(char[] s, int left, int right) {
if (left >= right) return;
char tmp = s[left];
s[left++] = s[right];
s[right--] = tmp;
helper(s, left, right);
}

Kotlin:

fun reverseString(s: CharArray): Unit {
helper(0, s.lastIndex, s)
}

fun helper(i: Int, j: Int, s: CharArray) {
if (i >= j) {
return
}
val t = s[j]
s[j] = s[i]
s[i] = t
helper(i + 1, j - 1, s)
}

java 代码通过大量输入的测试,但 kotlin 代码会导致 StackOverFlowError 除非我在 helper 函数之前添加 tailrec 关键字在 Kotlin 中。

我想知道为什么这个函数在 java 中有效,并且在带有 tailrec 的 kolin 中也有效,但在没有 tailrec 的 kotlin 中无效?

附注:我知道 tailrec 做什么

最佳答案

I want to know why this function works in java and also in kotlin with tailrec but not in kotlin without tailrec?

简短的回答是因为您的 Kotlin 方法比 JAVA 方法“更重”。每次调用时,它都会调用另一个“引发”StackOverflowError 的方法。因此,请参阅下面更详细的解释。

reverseString() 的 Java 字节码等效项

我分别检查了 KotlinJAVA 中您的方法的字节码:

JAVA 中的 Kotlin 方法字节码

...
public final void reverseString(@NotNull char[] s) {
Intrinsics.checkParameterIsNotNull(s, "s");
this.helper(0, ArraysKt.getLastIndex(s), s);
}

public final void helper(int i, int j, @NotNull char[] s) {
Intrinsics.checkParameterIsNotNull(s, "s");
if (i < j) {
char t = s[j];
s[j] = s[i];
s[i] = t;
this.helper(i + 1, j - 1, s);
}
}
...

JAVA中的JAVA方法字节码

...
public void reverseString(char[] s) {
this.helper(s, 0, s.length - 1);
}

public void helper(char[] s, int left, int right) {
if (left < right) {
char temp = s[left];
s[left++] = s[right];
s[right--] = temp;
this.helper(left, right, s);
}
}
...

因此,有两个主要区别:

  1. Intrinsics.checkParameterIsNotNull(s, "s") 会在 Kotlin 版本中为每个 helper() 调用。
  2. JAVA 方法中的左索引和右索引会递增,而在 Kotlin 中,会为每个递归调用创建新索引。

那么,让我们测试一下 Intrinsics.checkParameterIsNotNull(s, "s") 单独如何影响行为。

测试两种实现

我为这两种情况创建了一个简单的测试:

@Test
public void testJavaImplementation() {
char[] chars = new char[20000];
new Example().reverseString(chars);
}

@Test
fun testKotlinImplementation() {
val chars = CharArray(20000)
Example().reverseString(chars)
}

对于JAVA,测试成功,没有任何问题,而对于Kotlin,则由于StackOverflowError而惨败。但是,在我将 Intrinsics.checkParameterIsNotNull(s, "s") 添加到 JAVA 方法后,它也失败了:

public void helper(char[] s, int left, int right) {
Intrinsics.checkParameterIsNotNull(s, "s"); // add the same call here

if (left >= right) return;
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
helper(s, left + 1, right - 1);
}

结论

您的 Kotlin 方法的递归深度较小,因为它在每一步都会调用 Intrinsics.checkParameterIsNotNull(s, "s"),因此比它的 更重JAVA对应。如果您不想要此自动生成的方法,那么您可以在编译期间禁用空检查,如回答 here 所示。

但是,既然您了解 tailrec 带来的好处(将递归调用转换为迭代调用),您应该使用该调用。

关于java - 递归方法调用在 kotlin 中会导致 StackOverFlowError,但在 java 中不会,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59454209/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com