web-dev-qa-db-fra.com

Équivalent du foldLeft de Scala en Java 8

Quel est l'équivalent de la grande foldLeft de Scala dans Java 8?

J'ai été tenté de penser que c'était reduce, mais réduire doit renvoyer quelque chose de type identique à ce qu'il réduit. 

Exemple:

import Java.util.List;

public class Foo {

    // this method works pretty well
    public int sum(List<Integer> numbers) {
        return numbers.stream()
                      .reduce(0, (acc, n) -> (acc + n));
    }

    // this method makes the file not compile
    public String concatenate(List<Character> chars) {
        return chars.stream()
                    .reduce(new StringBuilder(""), (acc, c) -> acc.append(c)).toString();
    }
}

Le problème dans le code ci-dessus est le accumulator: new StringBuilder("")

Ainsi, quelqu'un pourrait-il m'indiquer l'équivalent approprié de foldLeft/réparer mon code?

18
GA1

Mettre à jour: 

Voici la tentative initiale pour obtenir votre code corrigé:

public static String concatenate(List<Character> chars) {
        return chars
                .stream()
                .reduce(new StringBuilder(),
                                StringBuilder::append,
                                StringBuilder::append).toString();
    }

Il utilise la méthode suivante reduction :

<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

Cela peut sembler déroutant, mais si vous examinez les javadocs, une explication intéressante peut vous aider à saisir rapidement les détails. La réduction est équivalente au code suivant:

U result = identity;
for (T element : this stream)
     result = accumulator.apply(result, element)
return result;

Pour une explication plus détaillée, veuillez vérifier cette source

Cette utilisation n’est pas correcte car elle enfreint le contrat de réduction qui stipule que l’accumulateur doit être une fonction sans état associative, non interférant, permettant d’incorporer un élément supplémentaire dans un résultat. En d'autres termes, puisque l'identité est modifiable, le résultat sera cassé en cas d'exécution parallèle.

Comme indiqué dans les commentaires ci-dessous, une option correcte utilise la réduction comme suit:

return chars.stream().collect(
     StringBuilder::new, 
     StringBuilder::append, 
     StringBuilder::append).toString();

Le fournisseur StringBuilder::new sera utilisé pour créer des conteneurs réutilisables qui seront combinés ultérieurement. 

10
Lachezar Balev

Il n'y a pas d'équivalent de foldLeft dans l'API Stream de Java 8. Comme d'autres l'ont noté, reduce(identity, accumulator, combiner) s'approche, mais ce n'est pas équivalent à foldLeft car il nécessite que le type résultant B se combine avec lui-même et soit associatif (en d'autres termes, ressemble à un monoïde), propriété que tous les types n'ont pas. 

Il existe également une demande d’amélioration à cet effet: add Stream.foldLeft () terminal operation

Pour voir pourquoi la réduction ne fonctionne pas, considérons le code suivant, dans lequel vous avez l'intention d'exécuter une série d'opérations arithmétiques commençant par un nombre donné:

val arithOps = List(('+', 1), ('*', 4), ('-', 2), ('/', 5))
val fun: (Int, (Char, Int)) => Int = {
  case (x, ('+', y)) => x + y
  case (x, ('-', y)) => x - y
  case (x, ('*', y)) => x * y
  case (x, ('/', y)) => x / y
}
val number = 2
arithOps.foldLeft(number)(fun) // ((2 + 1) * 4 - 2) / 5

Si vous avez essayé d'écrire reduce(2, fun, combine), quelle fonction de combinateur pourriez-vous utiliser pour combiner deux nombres? Ajouter clairement les deux chiffres ne résout pas le problème. De plus, la valeur 2 n'est clairement pas un élément identity.

Notez qu'aucune opération nécessitant une exécution séquentielle ne peut être exprimée en reduce. foldLeft est en réalité plus générique que reduce: vous pouvez implémenter reduce avec foldLeft mais vous ne pouvez pas implémenter foldLeft avec reduce

14
dzs

La méthode que vous recherchez est Java.util.Stream.reduce , en particulier la surcharge avec trois paramètres, identité, accumulateur et fonction binaire. C'est l'équivalent correct de la variable foldLeft de Scala.

Cependant, vous êtes pas autorisé à utiliser la variable reduce de Java de cette manière, et également la variable foldLeft de Scala. Utilisez collect à la place.

6
Jörg W Mittag

D'autres sont corrects il n'y a pas d'équivalent cependant. Voici un util qui vient près-

<U, T> U foldLeft(Collection<T> sequence, U identity, BiFunction<U, ? super T, U> accumulator) {
    U result = identity;
    for (T element : sequence)
        result = accumulator.apply(result, element);
    return result;
}

votre cas en utilisant la méthode ci-dessus ressemblerait

public String concatenate(List<Character> chars) {
    return foldLeft(chars, new StringBuilder(""), StringBuilder::append).toString();
}

Ou sans la méthode lambda ref sugar, 

public String concatenate(List<Character> chars) {
    return foldLeft(chars, new StringBuilder(""), (stringBuilder, character) -> stringBuilder.append(character)).toString();
}
0
Gagandeep Kalra