代碼托管:https://github.com/stopping5/javaBase/blob/master/src/com/company/java8/StreamTest.java
一、什麼是Stream
1、定義
Java 8引入了一套新的類庫,位于包java.util.stream下,稱為Stream API。這套API操作資料的思路不同于我們之前介紹的容器類API,它們是函數式的,非常簡潔、靈活、易讀。
A sequence of elements supporting sequential and parallel aggregate operations.
支援順序和并行聚合操作的元素序列。
在java8中java.util.Collection新增了兩個預設方法,它們可以傳回一個Stream。順序流就是由一個線程執行操作。而并行流背後可能有多個線程并行執行。
//通過該集合傳回集合源的流
default Stream<E> stream()
//通過該集合傳回并行的流
default Stream<E> parallelStream()
2、Stream特性
- Stream不會存儲元素
- Stream不改變源對象,會根據相關操作傳回一個持有結果的新對象
- Stream 操作是延遲執行的,意味着等待結果的時候才執行。
二、建立Stream
通過集合、數組建立一個資料源。
通過 java.util.Collection.stream()
方法用集合建立流
java.util.Collection.stream()
List<String> list = Arrays.asList("a", "b", "c");
// 建立一個順序流
Stream<String> stream = list.stream();
// 建立一個并行流
Stream<String> parallelStream = list.parallelStream();
使用 java.util.Arrays.stream(T[] array)
方法用數組建立流
java.util.Arrays.stream(T[] array)
int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);
使用 Stream
的靜态方法: of()、iterate()、generate()
Stream
of()、iterate()、generate()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println); // 0 2 4 6 8 10
Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);
三、中間操作
中間操作的目的主要是對資料進行操作,例如映射、統計、過濾等…
1、篩選操作
Filter
傳回predicate比對的流
案例:篩選年齡大于18并且薪資大于8000的員工
private static void filterDemo(List<Employee> employees) {
employees.stream()
//年齡大于18
.filter((e)->{
System.out.println("篩選大于18");
return e.getAge() > 18;
})
//并且薪資大于8000
.filter((e)->{
System.out.println("篩選大于8000");
return e.getSalary() > 8000;
})
.limit(2)//&& 操作
.forEach(
(e)->{
System.out.println(e.toString());
}
);
//篩選大于18
//篩選大于18
//篩選大于18
//篩選大于8000
//Employee{name='張5', age=28, salary=18000.0}
//篩選大于18
//篩選大于8000
//Employee{name='張6', age=19, salary=8400.0}
}
2、映射操作
- Map
接收lambda,将元素轉換成其他形式和提取資訊。接收一個函數作為參數,該函數會被應用到每個元素上。相當于資料庫中提取一列的資料。
<R> Stream<R> map(Function<? super T,? extends R> mapper)
------------------------------------------------------------
Type Parameters:
R - The element type of the new stream
Parameters:
mapper - a non-interfering, stateless function to apply to each element
Returns:
the new stream
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-vY8vZVQi-1625380843937)(http://img.stopping.top/20210704123510.svg)]
類似的還有:mapToInt、mapToLong、mapToDouble
案例:在Employee擷取所有年齡組成新的流。
這裡通過employee.getAge()函數應用到每個元素上的結果。
private static void mapDemo(List<Employee> employees) {
employees.stream().map(Employee::getAge).forEach(System.out::println);
}
//18 - 13 - 28 - 19 - 38 - 15 - 22
- FlatMap
接收一個函數作為參數,将流中每個值都換成另外一個流,然後把所有流連接配接成一個流。
案例
将流中的每個字元串輸出為字元。在FlatMap中直接就可以将每個元素的值轉化為一個流。在Map中通過調用flatMapStringToChar()将元素轉化為流,通過Stream<Stream(Charcyer)>相當于{{a,a,a},{b,b,b},{c,c,c}},而flatMap{a,a,a,b,b,b,c,c,c}兩者裡面的資料結構不一樣。
private static void flatMapDemo() {
System.out.println("Map實作···");
List<String> s = Arrays.asList("aaa","bbb","ccc");
//map實作每個元素字元串轉化為字元流輸出
Stream<Stream<Character>> ssc = s.stream().map(StreamTest::flatMapStringToChar);
ssc.forEach((sc)->{
sc.forEach(System.out::println);
});
System.out.println("flatMap實作···");
//flatMap 實作每個元素字元串轉化為字元流輸出
s.stream().flatMap(StreamTest::flatMapStringToChar).forEach(System.out::println);
}
public static Stream<Character> flatMapStringToChar(String s){
List<Character> lc = new ArrayList<>();
for (Character c : s.toCharArray()){
lc.add(c);
}
return lc.stream();
}
//Map實作···
//a
//a
//a
//b
//b
//b
//c
//c
//c
//flatMap實作···
//a
//a
//a
//b
//b
//b
//c
//c
//c
3、切片操作
limit 截取
限制傳回長度不超過
maxSize
的流
Stream<T> limit(long maxSize)
-------------------------------------------------------------------
Parameters:
maxSize - the number of elements the stream should be limited to
Returns:
the new stream
skip 丢棄
丢棄流中前
n
個元素,如果丢棄的元素大于流的本身的元素則傳回一個空流。
distinct 去重
去重,傳回流中不同的元素(根據 Object.equals(Object))流
案例
4、收集操作
四、終端操作
中間操作不觸發實際的執行,傳回值是Stream,而終端操作觸發執行,傳回一個具體的值,除了collect, Stream API的終端操作還有max、min、count、allMatch、anyMatch、noneMatch、findFirst、findAny、forEach、toArray、reduce等
MAX、MIN
Optional<T> max(Comparator<? super T> comparator)Optional<T> min(Comparator<? super T> comparator)-------------------------------------------------Parameters: comparator - a non-interfering, stateless Comparator to compare elements of this streamReturns: an Optional describing the maximum/minimum element of this stream, or an empty Optional if the stream is empty
案例:擷取最大/最小年齡的員工資訊
Count
傳回流中元素的個數
allMatch/anyMatch/noneMatch
這幾個函數都接受一個謂詞Predicate,傳回一個boolean值,用于判定流中的元素是否滿足一定的條件。
它們的差別是:
- allMatch:隻有在流中所有元素都滿足條件的情況下才傳回true。
- anyMatch:隻要流中有一個元素滿足條件就傳回true。
- noneMatch:隻有流中所有元素都不滿足條件才傳回true。
如果流為空,那麼這幾個函數的傳回值都是true。
findFirst/findAny
Optional<T> findFirst()傳回流的第一個元素的 Optional,如果流為空,則傳回空的 Optional。Optional<T> findAny()傳回流的某些元素的 Optional,如果流為空,則傳回空的 Optional。-------------------------------------------------------------------Returns: an Optional describing some element of this stream, or an empty Optional if the stream is emptyThrows: NullPointerException - if the element selected is null
foreach
周遊流中的元素,foreach和佛reachOrdered的差別:在并行流中,forEach不保證處理的順序,而forEachOrdered會保證按照流中元素的出現順序進行處理。
reduce
reduce代表歸約或者叫折疊,它是max/min/count的更為通用的函數,将流中的元素歸約為一個值。例如例如 sum()、max() 或 count()都是歸約函數。
Collectors
java.util.stream.Collectors 實作了Collector接口,提供了很多有用的歸約操作,例如将中間操作累積為集合、連接配接資料、分組、統計等。
用法示例:
groupingBy 分組
分組類似于資料庫查詢語言SQL中的group by語句,它将元素流中的每個元素分到一個組,可以針對分組再進行處理和收集。
案例
建立一個集合,裡面包括每個使用者的名稱、年齡、手機号。現在通過年齡來實作分組。
User.java 實體類
public class User { /** * 使用者名 */ private String username; /** * 年齡 */ private Integer age; /** * 手機号 */ private String phone;}
public static void main(String[] args) { List<User> users = Arrays.asList(new User[]{ new User("tom",18,"13141234567"), new User("job",20,"13141234567"), new User("gogo",18,"13141234567") }); Map<Integer,List<User>> userMap = users.stream().collect(Collectors.groupingBy(User::getAge)); userMap.forEach((i,user)->{ System.out.println("age:"+i+","+"user:"+user.toString()); });}
結果
age:18,user:[User{username='tom', age=18, phone='13141234567'}, User{username='gogo', age=18, phone='13141234567'}]age:20,user:[User{username='job', age=20, phone='13141234567'}]
使用Stream group方法等同于下面方法實作,很顯然Steam Group更加的簡潔。
/** * 非Stream group 實作 * */private static void userGroup(List<User> users) { Map<Integer,List<User>> userMap = new HashMap<>(); for (User u : users){ Integer age = u.getAge(); if (userMap.containsKey(age)){ userMap.get(age).add(u); }else{ List<User> user = new ArrayList<>(); user.add(u); userMap.put(age,user); } } userMap.forEach((i,user)->{ System.out.println("age:"+i+","+"user:"+user.toString()); });}