在學習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;
}
複製代碼
上述方法存在三個主要問題:數據結構
爲了克服上述全部缺點,引入了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的核心概念,而後咱們將經過一些示例來了解最經常使用的方法。函數
集合是用於保存值的內存數據結構,在開始使用集合以前,全部值都應已填充。 而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。
Java 8 Stream API方法中一些經常使用的功能接口是:
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);
}
複製代碼
爲了處理原始類型,有特定的函數接口-ToIntFunction
,ToLongFunction
,ToDoubleFunction
,ToIntBiFunction
,ToLongBiFunction
,ToDoubleBiFunction
,LongToIntFunction
,LongToDoubleFunction
,IntToLongFunction
,IntToDoubleFunction
等。
使用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表示一個函數,它接受一種類型的參數,返回一個布爾值,用於從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表示一個接受單個輸入參數且不返回結果的操做。 它可用於對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表明一種操做,經過該操做咱們能夠在流中生成新值。
@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是一個容器對象,可能包含也可能不包含非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() 複製代碼
中間操做: 返回新Stream的Java Stream API操做稱爲中間操做。 在大多數狀況下,這些操做本質上都是惰性的,所以中間操做只在終端操做出現時纔會執行。 中間操做不會生成最終結果。 經常使用的中間操做是filter
和map
。
終端操做:返回結果或產生反作用的Java 8 Stream API操做。 一旦在流上調用了終端方法,該流就會被消耗,此後咱們將沒法使用該流。 終端操做會在返回結果以前處理流中的全部元素。 經常使用的終端方法是forEach
,toArray
,min
,max
,findFirst
,anyMatch
,allMatch
等。您能夠從返回類型中識別終端方法,它們永遠不會返回Stream。
對於一個 intermediate 操做,若是它接受的是一個無限大(infinite/unbounded)的 Stream,但返回一個有限的新 Stream。例如limit()和skip()是兩個短路中間操做
對於一個 terminal 操做,若是它接受的是一個無限大的 Stream,但能在有限的時間計算出結果。 例如anyMatch
,allMatch
,noneMatch
,findFirst
和findAny
是短路終端操做。
咱們能夠經過幾種方法從數組和集合建立Java流。 讓咱們用簡單的例子來研究它們。
Stream<Integer> stream = Stream.of(1,2,3,4);
複製代碼
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>
複製代碼
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();
複製代碼
Stream<String> stream1 = Stream.generate(() -> {return "abc";});
Stream<String> stream2 = Stream.iterate("abc", (i) -> i);
複製代碼
LongStream is = Arrays.stream(new long[]{1,2,3,4});
IntStream is2 = "abc".chars();
複製代碼
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}
複製代碼
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]
複製代碼
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 "
複製代碼
Stream<String> names = Stream.of("aBc", "d", "ef");
System.out.println(names.map(s -> {
return s.toUpperCase();
}).collect(Collectors.toList()));
//prints [ABC, D, EF]
複製代碼
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]
複製代碼
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]
複製代碼
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);
複製代碼
Stream<Integer> numbers1 = Stream.of(1,2,3,4,5);
System.out.println("Number of elements in stream="+numbers1.count()); //5
複製代碼
Stream<Integer> numbers2 = Stream.of(1,2,3,4,5);
numbers2.forEach(i -> System.out.print(i+",")); //1,2,3,4,5,
複製代碼
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
複製代碼