1前書き

この記事では、

Stream

の実装がJavaとVavrでどのように異なるのかを調べます。

この記事は

Java Stream API



Vavrライブラリ

の両方の基本に精通していることを前提としています。


2比較

どちらの実装も遅延シーケンスの同じ概念を表しますが、詳細は異なります。

  • Java

    Streams

    は、堅牢な並列処理を念頭に置いて構築され、並列化を容易にサポートします。一方、Vavrの実装では、一連のデータを扱う手間がかかり、並列処理をネイティブでサポートしていません(ただし、インスタンスをJava実装に変換することで実現できます)。

Java Streamsが


Spliterator


インスタンスによって支えられているのはこのためです。はるかに古い

Iterator

とVavrの実装へのアップグレードは前述の

Iterator

(少なくとも最新の実装の一つで)によって支えられています。

どちらの実装もバッキングデータ構造と大まかに結び付いており、基本的にはストリームが通過するデータのソースの上に位置していますが、Vavrの実装は「イテレータベース」であるため、ソースコレクションの同時変更を許容しません。

  • Javaがストリームソースを処理することで、https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#NonInterference[well -behaved stream sources]にアクセスできるようになります。端末ストリーム操作が実行される前に変更されます。 **

基本的な設計上の違いにかかわらず、Vavrはそのストリーム(およびその他のデータ構造)をJava実装に変換する非常に堅牢なAPIを提供します。


3追加機能

ストリームとその要素を扱うためのアプローチは、JavaとVavrの両方でそれらを扱うことができる方法に興味深い違いをもたらします


3.1. ランダム要素アクセス

便利なAPIとアクセスメソッドを要素に提供することは、Vavrが本当にJava APIを照らしている分野の1つです。たとえば、Vavrにはランダムな要素アクセスを提供するメソッドがいくつかあります。


  • __https://static.javadoc.io/io.vavr/vavr/0.9.2/io/vavr/collection/Stream.html#get-int-[get()]

    __provides

ストリームの要素へのインデックスベースのアクセス。

標準と同じインデックス位置機能を提供します。
Java

リスト

**


insert()

指定された位置でストリームに要素を追加する機能を提供します。

提供された引数をストリームのすべての要素の間に挿入します。

ストリーム内からアイテムを見つけて返します。 Javaはhttps://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#noneMatch-java.util.function.Predicate-[

noneMatched

]を提供します。素子。


  • __https://static.javadoc.io/io.vavr/vavr/0.9.2/io/vavr/collection/Stream.html#update-int-java.util.function.Function-[update()]

    will

与えられたインデックスで要素を置き換えます。これは置換を計算する関数も受け取ります。

ソートされた






ストリームでアイテムを探します(ソートされていないストリームは未定義の結果になります)

重要なのは、この機能は依然として検索に対して直線的なパフォーマンスを持つデータ構造によって支えられているということです。


3.2. 並列処理と並行修正

VavrのStreamsはJavaの

parallel()

メソッドのようにネイティブに並列処理をサポートしていませんが、____https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Value.html#toJavaParallelStream-があります。 – [toJavaParallelStream] – ソースVavrストリームの並列化されたJavaベースのコピーを提供するメソッド。

Vavrストリームの相対的な弱さの領域は、原則としてhttps://docs.oracle.com/javase/8/docs/api/java/util/stream/packages-summary.html#NonInterference[


Non-Interferenceにあります。


。]

簡単に言えば、




Javaストリームを使用すると、端末操作が呼び出されるまで、基礎となるデータソースを変更することができます。特定のJavaストリームに対して端末操作が呼び出されていない限り、ストリームは基になるデータソースへの変更を取得できます。

List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
Stream<Integer> intStream = intList.stream();//form the stream
intList.add(5);//modify underlying list
intStream.forEach(i -> System.out.println("In a Java stream: " + i));

最後の追加がストリームからの出力に反映されていることがわかります。この動作は、変更がストリームパイプラインの内部であるか外部であるかにかかわらず一貫しています。

in a Java stream: 1
in a Java stream: 2
in a Java stream: 3
in a Java stream: 5

Vavrストリームはこれを許容できないことがわかりました。

Stream<Integer> vavrStream = Stream.ofAll(intList);
intList.add(5)
vavrStream.forEach(i -> System.out.println("in a Vavr Stream: " + i));

私たちが得るもの:

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
  at java.util.ArrayList$Itr.next(ArrayList.java:851)
  at io.vavr.collection.StreamModule$StreamFactory.create(Stream.java:2078)

Java標準では、Vavrストリームは「行儀よく」動作しません。 Vavrはプリミティブなバッキングデータ構造でよりよく振る舞います:

int[]aStream = new int[]{1, 2, 4};
Stream<Integer> wrapped = Stream.ofAll(aStream);

aStream[2]= 5;
wrapped.forEach(i -> System.out.println("Vavr looped " + i));

お渡しします。

Vavr looped 1
Vavr looped 2
Vavr looped 5


3.3. 短絡演算と

flatMap()



flatMapは、

map

操作のように

ストリーム処理の中間操作です – どちらの実装もhttps://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#に従います。 StreamOps[中間ストリーム操作の規約] – 基礎となるデータ構造からの処理は、端末操作が呼び出されるまで行われません。

しかしJDK 8と9はhttps://bugs.java.com/bugdatabase/view

bug.do?bug

id=8075939[バグ]を特徴としています。


_findFirst


または

limit_

簡単な例:

Stream.of(42)
  .flatMap(i -> Stream.generate(() -> {
      System.out.println("nested call");
      return 42;
  }))
  .findAny();

上記のスニペットでは、ネストされた

Stream.

から単一の要素を取得するのではなく、

flatMap

が熱心に評価されるため、

findAny

から結果が得られることはありません。

  • このバグの修正はJava 10で提供されました。**

Vavrの____flatMapは同じ問題を抱えておらず、機能的に同様の操作はO(1)で完了する

Stream.of(42)
  .flatMap(i -> Stream.continually(() -> {
      System.out.println("nested call");
      return 42;
  }))
  .get(0);


3.4. コアVavr機能

一部の地域では、JavaとVavrの間に一対一の比較がないだけです。 Vavrは、Javaには直接匹敵しない機能でストリーミング体験を向上させます(または少なくともかなりの手作業が必要です)。

提供された

__Iterableからのものでストリーム内のアイテムをアップします。


この
操作はJDK-8でサポートされていましたが、

since
build-93

の後に削除されました。
**



partition()


__will

述語が与えられると、ストリームの内容を2つのストリームに分割します。


  • __https://static.javadoc.io/io.vavr/vavr/0.9.2/io/vavr/collection/Stream.html#permutations–[permutation()]

namedは、ストリームの要素の置換(すべての可能な一意の順序)を計算します。


  • __https://static.javadoc.io/io.vavr/vavr/0.9.2/io/vavr/collection/Stream.html#combinations-int-[組み合わせ()]

    __gives

ストリームの組み合わせ(つまり可能な項目の選択)。

提供された分類子によって分類された、元のストリームからの要素を含むストリームの

__Map

__ofを返します。

Vavrでは、

compareTo

lambda式を受け入れるバリアントを提供することによって、Javaバージョンを改良しています。

高度な機能のサポートはJava SEストリームではやや意欲的ではありませんが、https://github.com/javaee/el-spec/blob/master/spec/pdf/EL3.0.PFD.pdf[式言語3.0]は奇妙にも標準のJDKストリームよりもはるかに多くの機能をサポートします。


4ストリーム操作

Vavrはストリームの内容を直接操作することを可能にします。


  • 既存のVavrストリームに挿入する

Stream<String> vavredStream = Stream.of("foo", "bar", "baz");
vavredStream.forEach(item -> System.out.println("List items: " + item));
Stream<String> vavredStream2 = vavredStream.insert(2, "buzz");
vavredStream2.forEach(item -> System.out.println("List items: " + item));


  • ストリームからアイテムを削除する

Stream<String> removed = inserted.remove("buzz");


  • キューベースの操作

Vavrのストリームはキューによってサポートされているので、一定時間の

prepend

および

__append

操作を提供します。

ただし、

Vavrストリームに加えられた変更は、そのストリームが作成されたデータソースには反映されません。


5結論

VavrとJavaにはそれぞれ長所があり、それぞれのライブラリがその設計目標 – Javaを安価な並列処理に、そしてVavrを便利なストリーム操作にコミットすることを実証しました。

Vavrが独自のストリームとJavaの間で相互に変換することをサポートしているため、同じプロジェクト内の両方のライブラリの利点をあまり多くのオーバーヘッドなしに引き出すことができます。

このチュートリアルのソースコードはhttps://github.com/eugenp/tutorials/tree/master/java-vavr-stream[over on Github]から入手できます。