Dans JDK 8 avec lambda b93, il existait une classe Java.util.stream.Streams.Zip in b9 qui pouvait être utilisée pour les flux Zip (cela est illustré dans le tutoriel Exploration de Java8 Lambdas. Partie 1 de Dhananjay Nene ). Cette fonction:
Crée un flux combiné paresseux et séquentiel dont les éléments résultent de la combinaison des éléments de deux flux.
Cependant, en 98, cela a disparu. Enfait, la classe Streams
n’est même pas accessible dans Java.util.stream dans b98 .
Cette fonctionnalité a-t-elle été déplacée, et si oui, comment puis-je compresser des flux Zip de manière concise à l'aide de b98?
L’application à laquelle je pense est dans cette implémentation de Shen Java , où j'ai remplacé la fonctionnalité Zip dans
static <T> boolean every(Collection<T> c1, Collection<T> c2, BiPredicate<T, T> pred)
static <T> T find(Collection<T> c1, Collection<T> c2, BiPredicate<T, T> pred)
fonctionne avec un code plutôt prolixe (qui n'utilise pas les fonctionnalités de b98).
J'avais besoin de ça aussi, alors je viens de prendre le code source de b93 et de le mettre dans une classe "util". J'ai dû le modifier légèrement pour fonctionner avec l'API actuelle.
Pour référence, voici le code de travail (prenez-le à vos risques et périls ...):
public static<A, B, C> Stream<C> Zip(Stream<? extends A> a,
Stream<? extends B> b,
BiFunction<? super A, ? super B, ? extends C> zipper) {
Objects.requireNonNull(zipper);
Spliterator<? extends A> aSpliterator = Objects.requireNonNull(a).spliterator();
Spliterator<? extends B> bSpliterator = Objects.requireNonNull(b).spliterator();
// Zipping looses DISTINCT and SORTED characteristics
int characteristics = aSpliterator.characteristics() & bSpliterator.characteristics() &
~(Spliterator.DISTINCT | Spliterator.SORTED);
long zipSize = ((characteristics & Spliterator.SIZED) != 0)
? Math.min(aSpliterator.getExactSizeIfKnown(), bSpliterator.getExactSizeIfKnown())
: -1;
Iterator<A> aIterator = Spliterators.iterator(aSpliterator);
Iterator<B> bIterator = Spliterators.iterator(bSpliterator);
Iterator<C> cIterator = new Iterator<C>() {
@Override
public boolean hasNext() {
return aIterator.hasNext() && bIterator.hasNext();
}
@Override
public C next() {
return zipper.apply(aIterator.next(), bIterator.next());
}
};
Spliterator<C> split = Spliterators.spliterator(cIterator, zipSize, characteristics);
return (a.isParallel() || b.isParallel())
? StreamSupport.stream(split, true)
: StreamSupport.stream(split, false);
}
Zip est l’une des fonctions fournies par bibliothèque de protonpack .
Stream<String> streamA = Stream.of("A", "B", "C");
Stream<String> streamB = Stream.of("Apple", "Banana", "Carrot", "Doughnut");
List<String> zipped = StreamUtils.Zip(streamA,
streamB,
(a, b) -> a + " is for " + b)
.collect(Collectors.toList());
assertThat(zipped,
contains("A is for Apple", "B is for Banana", "C is for Carrot"));
Si vous avez Gava dans votre projet, vous pouvez utiliser la méthode Streams.Zip (a été ajoutée dans Guava 21):
Renvoie un flux dans lequel chaque élément est le résultat du passage de l'élément correspondant de streamA et de streamB à function. Le flux résultant ne sera aussi long que le plus court des deux flux d’entrée; si un flux est plus long, ses éléments supplémentaires seront ignorés. Le flux résultant n'est pas fractionnable efficacement. Cela pourrait nuire aux performances parallèles.
public class Streams {
...
public static <A, B, R> Stream<R> Zip(Stream<A> streamA,
Stream<B> streamB, BiFunction<? super A, ? super B, R> function) {
...
}
}
Zipper deux flux en utilisant JDK8 avec lambda ( Gist ).
public static <A, B, C> Stream<C> Zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
final Iterator<A> iteratorA = streamA.iterator();
final Iterator<B> iteratorB = streamB.iterator();
final Iterator<C> iteratorC = new Iterator<C>() {
@Override
public boolean hasNext() {
return iteratorA.hasNext() && iteratorB.hasNext();
}
@Override
public C next() {
return zipper.apply(iteratorA.next(), iteratorB.next());
}
};
final boolean parallel = streamA.isParallel() || streamB.isParallel();
return iteratorToFiniteStream(iteratorC, parallel);
}
public static <T> Stream<T> iteratorToFiniteStream(Iterator<T> iterator, boolean parallel) {
final Iterable<T> iterable = () -> iterator;
return StreamSupport.stream(iterable.spliterator(), parallel);
}
Étant donné que je ne peux concevoir aucune utilisation de la compression sur des collections autres que celles indexées (listes) et que je suis un grand fan de simplicité, voici ma solution:
<A,B,C> Stream<C> zipped(List<A> lista, List<B> listb, BiFunction<A,B,C> zipper){
int shortestLength = Math.min(lista.size(),listb.size());
return IntStream.range(0,shortestLength).mapToObj( i -> {
return zipper.apply(lista.get(i), listb.get(i));
});
}
Les méthodes de la classe que vous avez mentionnée ont été déplacées vers l'interface Stream
en faveur des méthodes par défaut. Mais il semble que la méthode Zip
a été supprimée. Peut-être parce que le comportement par défaut pour des flux de tailles différentes n'est pas clair. Mais mettre en œuvre le comportement souhaité est simple:
static <T> boolean every(
Collection<T> c1, Collection<T> c2, BiPredicate<T, T> pred) {
Iterator<T> it=c2.iterator();
return c1.stream().allMatch(x->!it.hasNext()||pred.test(x, it.next()));
}
static <T> T find(Collection<T> c1, Collection<T> c2, BiPredicate<T, T> pred) {
Iterator<T> it=c2.iterator();
return c1.stream().filter(x->it.hasNext()&&pred.test(x, it.next()))
.findFirst().orElse(null);
}
La bibliothèque Lazy-Seq fournit la fonctionnalité Zip.
https://github.com/nurkiewicz/LazySeq
Cette bibliothèque est fortement inspirée de scala.collection.immutable.Stream
et vise à fournir une implémentation de séquence paresseuse immuable, thread-safe et facile à utiliser, éventuellement infinie.
Je suggère humblement cette mise en œuvre. Le flux résultant est tronqué au plus court des deux flux d'entrée.
public static <L, R, T> Stream<T> Zip(Stream<L> leftStream, Stream<R> rightStream, BiFunction<L, R, T> combiner) {
Spliterator<L> lefts = leftStream.spliterator();
Spliterator<R> rights = rightStream.spliterator();
return StreamSupport.stream(new AbstractSpliterator<T>(Long.min(lefts.estimateSize(), rights.estimateSize()), lefts.characteristics() & rights.characteristics()) {
@Override
public boolean tryAdvance(Consumer<? super T> action) {
return lefts.tryAdvance(left->rights.tryAdvance(right->action.accept(combiner.apply(left, right))));
}
}, leftStream.isParallel() || rightStream.isParallel());
}
public class Tuple<S,T> {
private final S object1;
private final T object2;
public Tuple(S object1, T object2) {
this.object1 = object1;
this.object2 = object2;
}
public S getObject1() {
return object1;
}
public T getObject2() {
return object2;
}
}
public class StreamUtils {
private StreamUtils() {
}
public static <T> Stream<Tuple<Integer,T>> zipWithIndex(Stream<T> stream) {
Stream<Integer> integerStream = IntStream.range(0, Integer.MAX_VALUE).boxed();
Iterator<Integer> integerIterator = integerStream.iterator();
return stream.map(x -> new Tuple<>(integerIterator.next(), x));
}
}
En utilisant la dernière bibliothèque Guava (pour la classe Streams
, vous devriez pouvoir faire
final Map<String, String> result =
Streams.Zip(
collection1.stream(),
collection2.stream(),
AbstractMap.SimpleEntry::new)
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
cyclops-react d'AOL, auquel je contribue, fournit également une fonctionnalité de compression, à la fois via une implémentation de flux étend , qui implémente également l'interface ReactiveSeq de Reactive-Stream, et via StreamUtils qui offre une grande partie des mêmes fonctionnalités via des méthodes statiques aux flux standard Java.
List<Tuple2<Integer,Integer>> list = ReactiveSeq.of(1,2,3,4,5,6)
.Zip(Stream.of(100,200,300,400));
List<Tuple2<Integer,Integer>> list = StreamUtils.Zip(Stream.of(1,2,3,4,5,6),
Stream.of(100,200,300,400));
Il offre également une compression plus générale basée sur les applications. Par exemple.
ReactiveSeq.of("a","b","c")
.ap3(this::concat)
.ap(of("1","2","3"))
.ap(of(".","?","!"))
.toList();
//List("a1.","b2?","c3!");
private String concat(String a, String b, String c){
return a+b+c;
}
Et même la possibilité d'associer chaque élément d'un flux à chaque élément d'un autre
ReactiveSeq.of("a","b","c")
.forEach2(str->Stream.of(str+"!","2"), a->b->a+"_"+b);
//ReactiveSeq("a_a!","a_2","b_b!","b_2","c_c!","c2")
C'est bien. Je devais compresser deux flux dans une carte, un flux étant la clé et l'autre la valeur.
Stream<String> streamA = Stream.of("A", "B", "C");
Stream<String> streamB = Stream.of("Apple", "Banana", "Carrot", "Doughnut");
final Stream<Map.Entry<String, String>> s = StreamUtils.Zip(streamA,
streamB,
(a, b) -> {
final Map.Entry<String, String> entry = new AbstractMap.SimpleEntry<String, String>(a, b);
return entry;
});
System.out.println(s.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())));
Sortie: {A = Pomme, B = Banane, C = Carotte}
Si quelqu'un en a encore besoin, il y a StreamEx.zipWith
fonction dans la bibliothèque streamex :
StreamEx<String> givenNames = StreamEx.of("Leo", "Fyodor")
StreamEx<String> familyNames = StreamEx.of("Tolstoy", "Dostoevsky")
StreamEx<String> fullNames = givenNames.zipWith(familyNames, (gn, fn) -> gn + " " + fn);
fullNames.forEach(System.out::println); // prints: "Leo Tolstoy\nFyodor Dostoevsky\n"