web-dev-qa-db-ja.com

ラムダ付きJDK8を使用したスト​​リームの圧縮(Java.util.stream.Streams.Zip)

ラムダb93を備えたJDK 8には、クラス b93のJava.util.stream.Streams.Zip があり、これをストリームのZipに使用できました(これはチュートリアルで説明されています Java8 Lambdasの探索。 Dhananjay Neneによるパート1 )。この機能:

要素が2つのストリームの要素を結合した結果であるレイジーでシーケンシャルな結合Streamを作成します。

ただし、b98ではこれはなくなりました。実際、Streamsクラスは、 b98のJava.util.stream でもアクセスできません。

この機能は移動されましたか?その場合、b98を使用してストリームを簡潔に圧縮するにはどうすればよいですか?

私が念頭に置いているアプリケーションは、 このJava Shenの実装 です。ここで、Zip機能を置き換えました。

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

かなり冗長なコード(b98の機能を使用しない)で機能します。

134
artella

これも必要だったので、b93からソースコードを取り出して「util」クラスに入れました。現在のAPIを使用するには、少し変更する必要がありました。

参照用の作業コードは次のとおりです(ご自身の責任でお使いください...):

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は、 protonpackライブラリ で提供される機能の1つです。

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

プロジェクトにGuavaがある場合は、 Streams.Zip メソッドを使用できます(Guava 21で追加されました):

各要素が、streamAおよびstreamBのそれぞれの対応する要素を関数に渡した結果であるストリームを返します。結果のストリームは、2つの入力ストリームのうち短い方と同じ長さになります。 1つのストリームがより長い場合、その余分な要素は無視されます。結果のストリームは効率的に分割できません。これにより、並列パフォーマンスが損なわれる可能性があります。

 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

ラムダ付きのJDK8を使用して2つのストリームを圧縮する( 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

インデックス付きコレクション(リスト)以外のコレクションでzip圧縮を使用することは考えられないので、私はシンプルさの大ファンなので、これが私の解決策になります。

<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

あなたが言及したクラスのメソッドは、デフォルトのメソッドを支持してStreamインターフェース自体に移動されました。しかし、Zipメソッドは削除されたようです。おそらく、さまざまなサイズのストリームのデフォルトの動作がどうあるべきかが明確ではないからです。ただし、目的の動作を実装するのは簡単です。

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

Lazy-SeqライブラリーはZip機能を提供します。

https://github.com/nurkiewicz/LazySeq

このライブラリは、scala.collection.immutable.Streamに大きな影響を受けており、不変で、スレッドセーフで、使いやすいレイジーシーケンスの実装を提供することを目的としています。

6
Nick Siderakis

この実装を謙虚に提案します。結果のストリームは、2つの入力ストリームのうち短い方に切り捨てられます。

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

最新のGuavaライブラリ(Streamsクラス用)を使用すると、次のことができるはずです。

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

AOLの cyclops-react は、リアクティブストリームインターフェースReactiveSeqを実装する 拡張ストリーム実装 を介して、およびStreamUtilsを介して、Zip機能を提供します。標準のJava St​​reamsに静的メソッドを介して同じ機能の多くを提供します。

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

また、より一般化されたApplicativeベースの圧縮も提供します。例えば。

   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;
   }

あるストリームのすべてのアイテムを別のストリームのすべてのアイテムとペアリングする機能も

   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

これは素晴らしい。 1つのストリームがキーで、もう1つのストリームが値である2つのストリームをマップに圧縮する必要がありました

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())));

出力:{A = Apple、B = Banana、C = Carrot}

0
Gnana

まだこれが必要な場合は、 streamex ライブラリにStreamEx.zipWith関数があります。

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