web-dev-qa-db-fra.com

Compression des flux à l'aide de JDK8 avec lambda (Java.util.stream.Streams.Zip)

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).

134
artella

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);
}
71
siki

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"));
41
Dominic Fox

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) {
         ...
     }
 }
29
ZhekaKozlov

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);
}
25
Karol Król

É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));
     });        
}
16
Rafael

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);
}
10
Holger

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.

6
Nick Siderakis

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());
}
6
Doradus
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));
    }
}
1
robby_pelssers

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()));
1
Dan Borza

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")
1
John McClean

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}

0
Gnana

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"
0
const.grigoryev