理解java中的流Stream
概念:
流:代表任何有能力產出資料的資料來源物件或者是有能力接受資料的接收端物件<Thinking in Java>,換句話說:是對輸入或輸出裝置(檔案,網路,記憶體)的抽象。
流的本質: 資料傳輸,根據資料傳輸特性將流抽象為各種類,方便更直觀的進行資料操作。
Stream 使用一種類似用 SQL 語句從資料庫查詢資料的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。
Stream API可以極大提高Java程式設計師的生產力,讓程式設計師寫出高效率、乾淨、簡潔的程式碼。
流的建立:
//集合
list.stream();
list.parallelStream();
//陣列
Arrays.stream(array);//array是陣列
//數字
IntStream stream1;
LongStream stream2;
//無限流
Stream.generate(Supplier s);//返回無限順序無序流,其中每個元素由提供的供應商生成。這適用於生成恆定流,隨機元素流等。如:Stream.generate(new Random()::nextInt)
IntStream intStream = random.ints(0, 100);//建立一個無窮大的int型別的數字流,這些數字在0(包括0)和100(不包括100)之間
DoubleStream doubleStream = random.doubles();// 無窮大的double型別的數字流
流中間操作:
無狀態操作:
map 方法用於對映每個元素到對應的結果,以下程式碼片段使用 map 輸出了元素對應的平方數:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); // 獲取對應的平方數 List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
filter 過濾器
//過濾出空字串: List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");// 獲取空字串的數量 long count = strings.stream().filter(string -> string.isEmpty()).count();
peek 中間操作,類似foreach
Random random = new Random(); random.ints().limit(10).peek(System.out::println); //輸出10個數字
foreach 中間操作
Random random = new Random(); random.ints().limit(10).forEach(System.out::println); //輸出10個數字
unordered產生一個與當前流中元素相同的無序流, 當流本身就是無序或流已經無序, 會返回流本身
Stream.of(5, 1, 2, 6, 3, 7, 4).forEach(System.out::println); //
有狀態操作:
sorted 排序
Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println); //列印10條資料,並排序
distinct去重
Random random = new Random(); random.ints().limit(10).distinct().forEach(System.out::println); //列印10條資料,去重重複的資料條
limit/skip限制/跳過
Random random = new Random(); random.ints().limit(10).skip(5).forEach(System.out::println); //列印後5條資料
流終止操作:
短路操作:
findFirst/findAny:
在序列的流中,findAny和findFirst返回的,都是第一個物件;而在並行的流中,findAny返回的是最快處理完的那個執行緒的資料,所以說,在並行操作中,對資料沒有順序上的要求,那麼findAny的效率會比findFirst要快的
List<String> list = new ArrayList(); list.add("a"); list.add("b"); Optional<String> aa = list.stream().filter(str -> !str.equals("a")).findFirst(); System.out.println(aa.get()); //輸出b
boolean anyMatch(Predicate<? super T> predicate)只要有一個條件滿足即返回true
allMatch 必須全部都滿足才會返回true
noneMatch 全都不滿足才會返回true
List<String> list = new ArrayList(); list.add("a");list.add("b");list.add("c"); boolean f = list.stream().anyMatch(str->!str.equals("a")); //返回true
非短路操作:
forEach/forEachOrderd
Stream.of("AAA","BBB","CCC").parallel().forEach(s->System.out.println("Output:"+s)); //輸出順序不確定,因為是並行處理 Stream.of("AAA","BBB","CCC").parallel().forEachOrdered(s->System.out.println("Output:"+s)); //按順序輸出AAA BBB CCC
collect/toArray轉換成集合或者陣列
List a = Stream.of("AAA","BBB","CCC").collect(Collectors.toList());
Object[] b = Stream.of("AAA","BBB","CCC").toArray();
reduce根據指定的計算模型將Stream中的值計算得到一個最終結果
方法一:
Optional<T> reduce(BinaryOperator<T> accumulator);
- 對
Stream
中的資料通過累加器accumulator
迭代計算,最終得到一個Optional
對
Optional r = Stream.of(1,2,3).reduce( (a,b)->{a+=b;return a;}); //r.get()=6
方法二:
T reduce(T identity, BinaryOperator<T> accumulator);
- 提供一個跟Stream中資料同類型的初始值identity,通過累加器accumulator迭代計算Stream中的資料,得到一個跟Stream中資料相同型別的最終結果
int r = Stream.of(1,2,3).reduce(100,(a,b)->{a+=b;return a;}); // r=106
方法三:
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
- 提供一個不同於
Stream
中資料型別的初始值,通過累加器規則迭代計算Stream
中的資料,最終得到一個同初始值同類型的結果
ArrayList<Integer> r = Stream.of(1,2,3).reduce( newList,(a,b)->{a.add(b);return a;},(acc, item) -> null); System.out.println(r); //[1, 2, 3]
min/max/count
int r1 = Stream.of(1,2,3).max(Integer::compare).get();//3 int r2 = Stream.of(1,2,3).min(Integer::compare).get();//1 Long a = Stream.of(2,1,4,5,3).count(); //5
並行流:
parallelStream提供了流的並行處理,它是Stream的另一重要特性,其底層使用Fork/Join框架實現。簡單理解就是多執行緒非同步任務的一種實現。
適合沒有執行緒安全問題、較單純的資料處理任務。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); numbers.parallelStream().forEach(num->System.out.println(num)); //輸出: 6 5 7 8 3 9 4 2 1 (隨機的)
分組:
Map<String, List<Person>> collect = list.stream().collect(Collectors.groupingBy(person -> person.getName()));//根據姓名分組