Si vous ajoutez des importations statiques pour Stream.concat et Stream.of , le premier exemple peut être écrit comme suit:
Stream<Foo> stream = concat(stream1, concat(stream2, of(element)));
L'importation de méthodes statiques avec des noms génériques peut entraîner un code qui devient difficile à lire et à maintenir ( pollution de l'espace de noms ). Il serait donc préférable de créer vos propres méthodes statiques avec des noms plus significatifs. Cependant, pour la démonstration, je m'en tiendrai à ce nom.
public static <T> Stream<T> concat(Stream<? extends T> lhs, Stream<? extends T> rhs) {
return Stream.concat(lhs, rhs);
}
public static <T> Stream<T> concat(Stream<? extends T> lhs, T rhs) {
return Stream.concat(lhs, Stream.of(rhs));
}
Avec ces deux méthodes statiques (éventuellement en combinaison avec des importations statiques), les deux exemples pourraient être écrits comme suit:
Stream<Foo> stream = concat(stream1, concat(stream2, element));
Stream<Foo> stream = concat(
concat(stream1.filter(x -> x!=0), stream2).filter(x -> x!=1),
element)
.filter(x -> x!=2);
Le code est désormais nettement plus court. Cependant, je reconnais que la lisibilité ne s'est pas améliorée. J'ai donc une autre solution.
Dans de nombreuses situations, les collecteurs peuvent être utilisés pour étendre les fonctionnalités des flux. Avec les deux collecteurs en bas, les deux exemples pourraient être écrits comme suit:
Stream<Foo> stream = stream1.collect(concat(stream2)).collect(concat(element));
Stream<Foo> stream = stream1
.filter(x -> x!=0)
.collect(concat(stream2))
.filter(x -> x!=1)
.collect(concat(element))
.filter(x -> x!=2);
La seule différence entre la syntaxe souhaitée et la syntaxe ci-dessus est que vous devez remplacer concat (...) par collect (concat (...)) . Les deux méthodes statiques peuvent être implémentées comme suit (éventuellement utilisées en combinaison avec des importations statiques):
private static <T,A,R,S> Collector<T,?,S> combine(Collector<T,A,R> collector, Function<? super R, ? extends S> function) {
return Collector.of(
collector.supplier(),
collector.accumulator(),
collector.combiner(),
collector.finisher().andThen(function));
}
public static <T> Collector<T,?,Stream<T>> concat(Stream<? extends T> other) {
return combine(Collectors.toList(),
list -> Stream.concat(list.stream(), other));
}
public static <T> Collector<T,?,Stream<T>> concat(T element) {
return concat(Stream.of(element));
}
Bien entendu, cette solution présente un inconvénient qu'il convient de mentionner. collect est une opération finale qui consomme tous les éléments du flux. En plus de cela, le collecteur concat crée une ArrayList intermédiaire à chaque fois qu'il est utilisé dans la chaîne. Les deux opérations peuvent avoir un impact significatif sur le comportement de votre programme. Cependant, si la lisibilité est plus importante que les performances , cela peut toujours être une approche très utile.