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 StackOverFlowError
moins que j'ai ajouté les tailrec
mots - clés avant la helper
fonction Kotlin.
Je veux savoir pourquoi cette fonction fonctionne en Java et aussi à Kolin avec , tailrec
mais pas sans Kotlin tailrec
?
PS: Je sais ce que tailrec
faire
Je veux savoir pourquoi cette fonction fonctionne en java et aussi dans Kotlin avec
tailrec
mais pas dans Kotlin sanstailrec
?
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:
Intrinsics.checkParameterIsNotNull(s, "s")
est invoqué pour chaquehelper()
dans la Kotlin la version.- 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 tailrec
apporte (convertit votre appel récursif en un processus itératif) , vous devez utiliser celui - ci.