appel de méthode récursive dans la cause StackOverflowError Kotlin mais pas en java

hamid_c:

J'ai deux codes presque identiques en java et 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)
}

Le code java passe le test avec une entrée énorme , mais le code Kotlin la cause a StackOverFlowErrormoins que j'ai ajouté les tailrecmots - clés avant la helperfonction Kotlin.

Je veux savoir pourquoi cette fonction fonctionne en Java et aussi à Kolin avec , tailrecmais pas sans Kotlin tailrec?

PS: Je sais ce que tailrecfaire

Anatolii:

Je veux savoir pourquoi cette fonction fonctionne en java et aussi dans Kotlin avec tailrecmais pas dans Kotlin sans tailrec?

La réponse courte est parce que votre Kotlin méthode est que le « plus lourd » JAVA un. A chaque appel , il appelle une autre méthode qui « provoque » StackOverflowError. Alors, voir une explication plus détaillée ci - dessous.

équivalents Java bytecode pour reverseString()

J'ai vérifié le code octet pour vos méthodes Kotlin et JAVA en conséquence:

Procédé Kotlin bytecode JAVA

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

Procédé bytecode JAVA en 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);
    }
}
...

Donc, il y a 2 différences principales sommes:

  1. Intrinsics.checkParameterIsNotNull(s, "s")est invoqué pour chaque helper()dans la Kotlin la version.
  2. Indices gauche et à droite dans JAVA méthode incrémentée, alors que dans Kotlin nouveaux indices sont créés pour chaque appel récursif.

Ainsi, le test let comment Intrinsics.checkParameterIsNotNull(s, "s")seul affecte le comportement.

Testez les deux implémentations

J'ai créé un test simple pour les deux cas:

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

Et

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

Pour JAVA le test a réussi sans problème alors que pour Kotlin il a lamentablement échoué en raison d'un StackOverflowError. Cependant, après avoir ajouté Intrinsics.checkParameterIsNotNull(s, "s")à la JAVA méthode , il a aussi échoué:

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

Conclusion

Votre Kotlin méthode a une profondeur de récursivité plus petite qu'elle invoque Intrinsics.checkParameterIsNotNull(s, "s")à chaque étape et est donc plus lourd que son JAVA contrepartie. Si vous ne voulez pas cette méthode généré automatiquement, vous pouvez désactiver les contrôles null lors de la compilation comme réponse ici

Cependant, puisque vous comprenez quel avantage tailrecapporte (convertit votre appel récursif en un processus itératif) , vous devez utiliser celui - ci.

Je suppose que tu aimes

Origine http://10.200.1.11:23101/article/api/json?id=442998&siteId=1
conseillé
Classement