Java 8 Stream

java 8 lambda 精簡入門java

Java Stream

在學習Java Stream API以前,讓咱們看看爲何須要它。假設咱們須要遍歷一個整數列表,並找出全部大於10的整數之和。編程

在Java 8以前,咱們會這樣寫:數組

private static int sumIterator(List<Integer> list) {
    int sum = 0;
    Iterator<Integer> iterator = list.iterator();
    while (iterator.hasNext()) {
      int item = iterator.next();
      if (item > 10) {
        sum += item;
      }
    }
    return sum;
  }
複製代碼

上述方法存在三個主要問題:數據結構

  1. 咱們只想知道大於10的整數的總和,但咱們還必須提供迭代,這也稱爲外部迭代。
  2. 該程序本質上是按順序執行的,咱們沒法輕鬆地並行執行此操做。
  3. 須要不少代碼才能夠完成一個簡單的任務

爲了克服上述全部缺點,引入了Java 8 Stream API。 咱們可使用Java Stream API來實現內部迭代,這樣會更好,由於Java在幫咱們控制着迭代。app

內部迭代提供了一些功能,例如順序和並行執行,基於給定條件的過濾,映射等。框架

大多數Java 8 Stream API方法參數都是函數式接口,所以lambda表達式能夠很好地與它們一塊兒使用。 讓咱們看看如何使用Java Stream 在單行語句中編寫以上邏輯。less

private static int sumIterator(List<Integer> list) {
    return list.stream().filter(i -> i > 10).mapToInt(i -> i).sum();
  }
複製代碼

以上程序利用了Java框架的迭代,過濾,和映射方法,提升了效率。ide

首先,咱們將研究Java 8 Stream API的核心概念,而後咱們將經過一些示例來了解最經常使用的方法。函數

Collections and Java Stream

集合是用於保存值的內存數據結構,在開始使用集合以前,全部值都應已填充。 而Java Stream是按需計算的數據結構。post

Java Stream 不存儲數據,而是對源數據結構(集合和數組)進行操做,並生成可使用並執行特定操做的流水線數據。 例如,咱們能夠從列表建立流並根據條件對其進行過濾。

Java Stream 操做使用函數式接口,這使其很是適合使用lambda表達式的功能編程。 如您在上面的示例中看到的那樣,使用lambda表達式使咱們的代碼可讀性強且簡短。

Java 8 Stream內部迭代原理有助於實現某些流操做中的延遲查找。 例如,能夠延遲實施過濾,映射或重複刪除,從而實現更高的性能和優化範圍。

Java stream 流是可消耗的,所以沒法建立流引用以供未來使用。 因爲數據是按需提供的,所以沒法屢次重複使用同一數據流。

Java 8 Stream支持順序以及並行處理,並行處理對於實現大型集合的高性能很是有幫助。

全部Java Stream API接口和類都在java.util.stream包中。 因爲咱們可使用自動裝箱在集合中使用int之類的原始數據類型,而且這些操做可能會花費不少時間,所以有一些針對原始類型的特定類-IntStream,LongStream和DoubleStream。

Functional Interfaces in Java 8 Stream

Java 8 Stream API方法中一些經常使用的功能接口是:

Function and BiFunction

Function表示一個函數,它接受一種類型的參數並返回另外一種類型的參數。 Function <T,R>是通用形式,其中T是函數輸入的類型,R是函數結果的類型。

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}
複製代碼

BiFunction表示一個函數,它接受兩種類型的參數並返回另外一種類型的參數。 Function <T,U,R>是通用形式,其中T,U是函數輸入的類型,R是函數結果的類型。

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}
複製代碼

爲了處理原始類型,有特定的函數接口-ToIntFunctionToLongFunctionToDoubleFunctionToIntBiFunctionToLongBiFunctionToDoubleBiFunctionLongToIntFunctionLongToDoubleFunctionIntToLongFunctionIntToDoubleFunction等。

使用Function原始類型轉化相關的Function接口的Stream方法包括:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);
IntStream mapToInt(ToIntFunction<? super T> mapper);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
<A> A[] toArray(IntFunction<A[]> generator);
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
複製代碼

Predicate and BiPredicate 斷言

Predicate表示一個函數,它接受一種類型的參數,返回一個布爾值,用於從Java Stream中過濾元素

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}
複製代碼

Function同樣,爲了處理原始類型,它也有特定的函數接口-IntPredicate, DoubePredicate, LongPredicate

使用Predicate或BiPredicate做爲參數的一些Stream方法是:

Stream<T> filter(Predicate<? super T> predicate);
boolean anyMatch(Predicate<? super T> predicate);
boolean anyMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
複製代碼

Consumer and BiConsumer 消費者

Consumer表示一個接受單個輸入參數且不返回結果的操做。 它可用於對Java Stream的全部元素執行某些操做。

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}
複製代碼

使用Consumer,BiConsumer做爲參數的Java 8 Stream方法包括:

Stream<T> peek(Consumer<? super T> action);
void forEach(Consumer<? super T> action);
void forEachOrdered(Consumer<? super T> action);
複製代碼

Supplier 供應者

Supplier表明一種操做,經過該操做咱們能夠在流中生成新值。

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
複製代碼

Stream中帶有Supplier參數的一些方法是:

public static<T> Stream<T> generate(Supplier<T> s) <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
複製代碼

Java Optional

Java Optional是一個容器對象,可能包含也可能不包含非null值。 若是存在值,則isPresent() 將返回true,而get()將返回該值。 流終端操做返回Optional對象。 其中一些方法是:

Optional<T> reduce(BinaryOperator<T> accumulator) Optional<T> min(Comparator<? super T> comparator) Optional<T> max(Comparator<? super T> comparator) Optional<T> findFirst() Optional<T> findAny() 複製代碼

Java Stream Intermediate and Terminal Operations

中間操做: 返回新Stream的Java Stream API操做稱爲中間操做。 在大多數狀況下,這些操做本質上都是惰性的,所以中間操做只在終端操做出現時纔會執行。 中間操做不會生成最終結果。 經常使用的中間操做是filtermap

終端操做:返回結果或產生反作用的Java 8 Stream API操做。 一旦在流上調用了終端方法,該流就會被消耗,此後咱們將沒法使用該流。 終端操做會在返回結果以前處理流中的全部元素。 經常使用的終端方法是forEachtoArrayminmaxfindFirstanyMatchallMatch等。您能夠從返回類型中識別終端方法,它們永遠不會返回Stream。

Java Stream Short Circuiting Operations

  • 對於一個 intermediate 操做,若是它接受的是一個無限大(infinite/unbounded)的 Stream,但返回一個有限的新 Stream。例如limit()和skip()是兩個短路中間操做

  • 對於一個 terminal 操做,若是它接受的是一個無限大的 Stream,但能在有限的時間計算出結果。 例如anyMatchallMatchnoneMatchfindFirstfindAny是短路終端操做。

Java Stream Examples

建立java stream

咱們能夠經過幾種方法從數組和集合建立Java流。 讓咱們用簡單的例子來研究它們。

  1. 咱們可使用Stream.of()從類似類型的數據建立流。 例如,咱們能夠從一組int或Integer對象建立Java整數流。
Stream<Integer> stream = Stream.of(1,2,3,4);
複製代碼
  1. 咱們可使用Stream.of()從對象數組返回流。 請注意,它不支持自動裝箱,所以咱們沒法傳遞基本類型數組。
Stream<Integer> stream = Stream.of(new Integer[]{1,2,3,4}); 
//works fine

Stream<Integer> stream1 = Stream.of(new int[]{1,2,3,4}); 
//Compile time error, Type mismatch: cannot convert from Stream<int[]> to Stream<Integer>
複製代碼
  1. 咱們可使用Collection stream()建立順序流,並使用parallelStream()建立並行流。
List<Integer> myList = new ArrayList<>();
for(int i=0; i<100; i++) myList.add(i);
		
//sequential stream
Stream<Integer> sequentialStream = myList.stream();
		
//parallel stream
Stream<Integer> parallelStream = myList.parallelStream();
複製代碼
  1. 咱們可使用Stream.generate()和Stream.iterate()方法建立Stream。
Stream<String> stream1 = Stream.generate(() -> {return "abc";});
Stream<String> stream2 = Stream.iterate("abc", (i) -> i);
複製代碼
  1. 使用Arrays.stream()和String.chars()方法。
LongStream is = Arrays.stream(new long[]{1,2,3,4});
IntStream is2 = "abc".chars();
複製代碼

將Java Stream轉換爲Collection或Array

  1. 咱們可使用java Stream collect()方法從流中獲取List,Map或Set。
Stream<Integer> intStream = Stream.of(1,2,3,4);
List<Integer> intList = intStream.collect(Collectors.toList());
System.out.println(intList); //prints [1, 2, 3, 4]

intStream = Stream.of(1,2,3,4); //stream is closed, so we need to create it again
Map<Integer,Integer> intMap = intStream.collect(Collectors.toMap(i -> i, i -> i+10));
System.out.println(intMap); //prints {1=11, 2=12, 3=13, 4=14}
複製代碼
  1. 咱們可使用流toArray()方法從流中建立一個數組。
Stream<Integer> intStream = Stream.of(1,2,3,4);
Integer[] intArray = intStream.toArray(Integer[]::new);
System.out.println(Arrays.toString(intArray)); //prints [1, 2, 3, 4]
複製代碼

java stream 中間操做

  1. filter(): 篩選出符合條件的元素生成新的stream
List<Integer> myList = new ArrayList<>();
for(int i=0; i<100; i++) myList.add(i);
Stream<Integer> sequentialStream = myList.stream();

Stream<Integer> highNums = sequentialStream.filter(p -> p > 90); //filter numbers greater than 90
System.out.print("High Nums greater than 90=");
highNums.forEach(p -> System.out.print(p+" "));
//prints "High Nums greater than 90=91 92 93 94 95 96 97 98 99 "
複製代碼
  1. map(): 將流中的元素映射成新的類型,並生成新的stream
Stream<String> names = Stream.of("aBc", "d", "ef");
System.out.println(names.map(s -> {
		return s.toUpperCase();
	}).collect(Collectors.toList()));
//prints [ABC, D, EF]
複製代碼
  1. sorted(): 咱們可使用sorted()經過傳遞Comparator參數來對流元素進行排序
Stream<String> names2 = Stream.of("aBc", "d", "ef", "123456");
List<String> reverseSorted = names2.sorted(Comparator.reverseOrder()).collect(Collectors.toList());
System.out.println(reverseSorted); // [ef, d, aBc, 123456]

Stream<String> names3 = Stream.of("aBc", "d", "ef", "123456");
List<String> naturalSorted = names3.sorted().collect(Collectors.toList());
System.out.println(naturalSorted); //[123456, aBc, d, ef]
複製代碼
  1. flatMap(): flatMap 把 輸入流中的層級結構扁平化,就是將最底層元素抽出來放到一塊兒,最終 輸出的新Stream
Stream<List<Integer>> inputStream = Stream.of(
 Arrays.asList(1),
 Arrays.asList(2, 3),
 Arrays.asList(4, 5, 6)
 );
Stream<Integer> outputStream = inputStream.
flatMap((childList) -> childList.stream());
System.out.println(outputStream.collect(Collectors.toList()));
// [1, 2, 3, 4, 5, 6]
複製代碼

java stream 終端操做

  1. reduce(): 這個方法的主要做用是把 Stream 元素組合起來。它提供一個起始值(種子),而後依照運算規則(BinaryOperator),和前面 Stream 的第一個、第二個、第 n 個元素組合。從這個意義上說,字符串拼接、數值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就至關於
Integer sum = integers.reduce(0, (a, b) -> a+b);
// 或
Integer sum = integers.reduce(0, Integer::sum);
複製代碼

也存在沒有起始值的狀況,這時會把 Stream 的前面兩個元素組合起來,返回的是 Optional。

// 字符串鏈接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); 
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); 
// 求和,sumValue = 10, 有起始值, 起始值爲10
int sumValue = Stream.of(1, 2, 3, 4).reduce(10, Integer::sum);
// 求和,sumValue = 10, 無起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
// 過濾,字符串鏈接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").
 filter(x -> x.compareTo("Z") > 0).
 reduce("", String::concat);
複製代碼
  1. count(): 咱們可使用此終端操做來計算流中的項目數。
Stream<Integer> numbers1 = Stream.of(1,2,3,4,5);
   	
System.out.println("Number of elements in stream="+numbers1.count()); //5
複製代碼
  1. forEach():可用於迭代流。 咱們能夠用它代替迭代器
Stream<Integer> numbers2 = Stream.of(1,2,3,4,5);
numbers2.forEach(i -> System.out.print(i+",")); //1,2,3,4,5,
複製代碼
  1. match():讓咱們看一些Stream API中匹配方法的示例
Stream<Integer> numbers3 = Stream.of(1,2,3,4,5);
System.out.println("Stream contains 4? "+numbers3.anyMatch(i -> i==4));
//Stream contains 4? true

Stream<Integer> numbers4 = Stream.of(1,2,3,4,5);
System.out.println("Stream contains all elements less than 10? "+numbers4.allMatch(i -> i<10));
//Stream contains all elements less than 10? true

Stream<Integer> numbers5 = Stream.of(1,2,3,4,5);
System.out.println("Stream doesn't contain 10? "+numbers5.noneMatch(i -> i==10));
//Stream doesn't contain 10? true
複製代碼
相關文章
相關標籤/搜索