Il semble que j'ai du mal à comprendre comment Java compose les opérations de flux dans un pipeline de flux.
Lors de l'exécution du code suivant
public
static void main(String[] args) {
StringBuilder sb = new StringBuilder();
var count = Stream.of(new String[]{"1", "2", "3", "4"})
.map(sb::append)
.count();
System.out.println(count);
System.out.println(sb.toString());
}
La console imprime uniquement 4
. L' StringBuilder
objet a toujours la valeur ""
.
Lorsque j'ajoute l'opération de filtrage: filter(s -> true)
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
var count = Stream.of(new String[]{"1", "2", "3", "4"})
.filter(s -> true)
.map(sb::append)
.count();
System.out.println(count);
System.out.println(sb.toString());
}
La sortie devient:
4
1234
Comment cette opération de filtrage apparemment redondante modifie-t-elle le comportement du pipeline de flux composé?
java
java-stream
atalantus
la source
la source
Réponses:
L'
count()
opération de terminal, dans ma version du JDK, finit par exécuter le code suivant:S'il y a une
filter()
opération dans le pipeline d'opérations, la taille du flux, qui est connue initialement, ne peut plus être connue (carfilter
pourrait rejeter certains éléments du flux). Ainsi leif
bloc n'est pas exécuté, les opérations intermédiaires sont exécutées et le StringBuilder est ainsi modifié.D'un autre côté, si vous n'avez que
map()
dans le pipeline, le nombre d'éléments dans le flux est garanti identique au nombre initial d'éléments. Ainsi, le bloc if est exécuté et la taille est retournée directement sans évaluer les opérations intermédiaires.Notez que le lambda passé à
map()
viole le contrat défini dans la documentation: il est censé être une opération sans interférence et sans état, mais il n'est pas sans état. Donc, avoir un résultat différent dans les deux cas ne peut pas être considéré comme un bug.la source
flatMap()
peut-être être en mesure de changer le nombre d'éléments, était-ce la raison pour laquelle il était initialement impatient (maintenant paresseux)? Donc, l'alternative serait d'utiliserforEach()
et de compter séparément si,map()
dans sa forme actuelle, il viole le contrat, je suppose.4 1234
sans utiliser le filtre supplémentaire ou produire des effets secondaires dans l'opération map ()?int count = array.length; String result = String.join("", array);
Collectors.joining("")
Dans jdk-9, il était clairement documenté dans les documents java
Remarque sur l'API:
la source
Ce n'est pas à ça que sert .map. Il est censé être utilisé pour transformer un flux de "Something" en un flux de "Something Else". Dans ce cas, vous utilisez map pour ajouter une chaîne à un Stringbuilder externe, après quoi vous avez un flux de "Stringbuilder", dont chacun a été créé par l'opération de map en ajoutant un numéro au Stringbuilder d'origine.
Votre flux ne fait rien avec les résultats mappés dans le flux, il est donc parfaitement raisonnable de supposer que l'étape peut être ignorée par le processeur de flux. Vous comptez sur les effets secondaires pour faire le travail, ce qui brise le modèle fonctionnel de la carte. Vous seriez mieux servi en utilisant forEach pour ce faire. Faites le décompte en tant que flux distinct entièrement, ou placez un compteur à l'aide d'AtomicInt dans forEach.
Le filtre l'oblige à exécuter le contenu du flux car il doit maintenant faire quelque chose de significatif sur le plan théorique avec chaque élément de flux.
la source