Java流與Vavr流

1.簡介

在本文中,我們將研究Java和Vavr中Stream實現的不同之處。

本文假定您熟悉Java Stream API和Vavr庫的基礎。

2.比較

兩種實現都表示相同的惰性序列概念,但細節不同。

Java Streams的構建考慮到了強大的並行性,為並行化提供了輕鬆的支持。另一方面,Vavr實現有利於方便處理數據序列,並且不提供對並行性的本地支持(但是可以通過將實例轉換為Java實現來實現)。

這就是為什麼Java Streams由Spliterator實例支持的Spliterator –較舊的Iterator的升級和Vavr的實現由上述的Iterator (至少在最新的實現之一中)支持。

兩種實現都鬆散地與其支持的數據結構聯繫在一起,本質上是流所遍歷的數據源之上的立面,但是由於Vavr的實現是基於Iterator-,因此它不能容忍對源集合的並發修改。

Java對流源的處理使得可以在執行終端流操作之前修改行為良好的流源

儘管存在根本的設計差異,但Vavr提供了非常強大的API,可將其流(和其他數據結構)轉換為Java實現。

3.附加功能

處理流及其元素的方法導致我們在Java和Vavr中使用它們的方式產生有趣的差異

3.1。隨機元素訪問

提供方便的API和對元素的訪問方法是Vavr真正超越Java API的領域之一。例如,Vavr有一些提供隨機元素訪問的方法:

  • [get()](https://static.javadoc.io/io.vavr/vavr/0.9.2/io/vavr/collection/Stream.html#get-int-)提供對流元素的基於索引的訪問。
  • indexOf()提供與標準Java List.相同的索引定位功能List.
  • insert()提供了將元素添加到流中指定位置的功能。
  • intersperse()將在流的所有元素之間插入提供的參數。
  • find()將在流中找到並返回一個項目。 Java提供了noneMatchednoneMatched檢查元素的存在。
  • [update()](https://static.javadoc.io/io.vavr/vavr/0.9.2/io/vavr/collection/Stream.html#update-int-java.util.function.Function-)將替換給定索引處的元素。這也接受一個計算替換的函數。
  • search ()將找到排序的項目 流(未排序的流將產生不確定的結果)

重要的是,我們要記住,此功能仍由具有線性搜索性能的數據結構支持。

3.2。並行和並發修改

儘管Vavr的Streams本身並不像Java的parallel()方法那樣支持並行性,但是[toJavaParallelStream](https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Value.html#toJavaParallelStream--)方法提供了源Vavr流的基於Java的並行化副本。

Vavr流中相對較弱的區域是基於**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()

map操作一樣, flatMap,是流處理中的中間操作-兩種實現都遵循中間流操作的約定-在調用終端操作之前,不應進行來自基礎數據結構的處理。

然而,JDK 8和9具有一個錯誤,當與短路的中間操作(如findFirstlimit結合使用時,該錯誤會導致flatMap實現破壞此約定並進行急切的評估。

一個簡單的例子:

Stream.of(42)

 .flatMap(i -> Stream.generate(() -> {

 System.out.println("nested call");

 return 42;

 }))

 .findAny();

在上面的代碼段中,我們將永遠不會從findAny獲得結果,因為flatMap會被急切地求值,而不是簡單地從嵌套Stream.獲取單個元素Stream.

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之間沒有一對一的比較;只有在Java和Vavr之間才能進行比較。 Vavr通過Java中無法比擬的功能(或至少需要大量的手動工作)增強了流媒體體驗:

  • zip()將流中的項目與提供的Iterable.中的項目配對Iterable. JDK-8曾經支持此操作,但是在build-93之後已將其刪除。
  • 給定一個謂詞, [partition()](https://static.javadoc.io/io.vavr/vavr/0.9.2/io/vavr/collection/Stream.html#partition-java.util.function.Predicate-)將流的內容分為兩個流。
  • 命名為[permutation()](https://static.javadoc.io/io.vavr/vavr/0.9.2/io/vavr/collection/Stream.html#permutations--) ,將計算流元素的排列(所有可能的唯一順序)。
  • [combinations()](https://static.javadoc.io/io.vavr/vavr/0.9.2/io/vavr/collection/Stream.html#combinations-int-)給出流的組合(即,可能的項目選擇)。
  • groupBy將返回一個流Map ,其中包含來自原始流的元素(由提供的分類器分類)。
  • Vavr中的distinct方法通過提供一個接受compareTo表達式的變體在Java版本上進行了改進。

儘管Java SE流中對高級功能的支持似乎沒有靈感,但Expression Language 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流,它提供了恆定時間的prependappend操作。

但是,對Vavr流所做的更改不會傳播回創建該流的數據源。

5.結論

Vavr和Java都有各自的長處,我們已經展示了每個庫對其設計目標的承諾– Java代表廉價的並行性,而Vavr代表便利的流操作。

通過Vavr支持在其自己的流和Java之間來迴轉換,可以在同一項目中獲得這兩個庫的好處,而又不會產生很多開銷。

本教程的源代碼可在Github上獲得