Stream(流):
什麼是stream(流):Stream是一個來自數據源的元素隊列並支持聚合操作
元素:是特定類型的對象,形成一個隊列。 Java中的Stream並不會存儲元素,而是按需計算
數據源:流的來源。可以是集合,數組,I/O channel, 產生器generator 等
聚合操作:類似SQL語句一樣的操作,如:filter, map, reduce, find, match, sorted等
Stream操作的兩個基礎特徵:
Pipelining: 中間操作都會返回流對象本身
這樣多個操作可以串聯成一個管道,如同流式風格(fluent style)
這樣做可以對操作進行優化,比如延遲執行(laziness)和短路( short-circuiting)
內部迭代:
以前對集合遍歷都是通過Iterator或者For-Each的方式, 顯式的在集合外部進行迭代,這叫做外部迭代
Stream提供了內部迭代的方式, 通過訪問者模式(Visitor)實現
注意:
1. Stream自己不會存儲元素
2. Stream不會改變原對象,相反他們會返回一個持有結果的新Stream
3. Stream操作是延遲執行,這意味着他們會等到需要結果的時候才執行
Stream操作步驟:
1.創建流(即初始化流)
2.中間操作:Intermediate
一個流可以後面跟隨零個或多個intermediate操作
做出某種程度的數據映射/過濾,然後返回一個新的流,交給下一個操作使用
常見中間操作:
filter distinct sorted peek limit
skip parallel sequential unordered
map (mapToInt, flatMap 等)
3.終止操作:Terminal
一個流只能有一個terminal操作,執行多個terminal操作會報錯:stream has already been operated upon or closed
Terminal 操作的執行,纔會真正開始流的遍歷並且會生成一個結果或者一個 side effect
常見終止操作:
forEach forEachOrdered toArray reduce collect
min max count anyMatch allMatch noneMatch
findFirst findAny iterator
短路操作:Short-circuiting
對於一個intermediate 操作:
如果它接受的是一個無限大(infinite/unbounded)的Stream,但返回一個有限的新 Stream
對於一個terminal 操作:
如果它接受的是一個無限大的 Stream,但能在有限的時間計算出結果
常見短路操作:
anyMatch allMatch noneMatch findFirst findAny limit
流的創建:
方式一:Collection接口相關方法 將集合轉換爲流
default Stream< E> stream(); //返回一個串行流
default Stream< E> parallelStream(); //返回一個並行流
示例:
List<String> list = Arrays.asList("a","ab","abc","abcd","dh","");
Stream<String> listToStream = list.stream();//串行流
Stream<String> parallelStream = list.parallelStream();//並行流
並行流通過默認的ForkJoinPool,可能提高你的多線程任務的速度。並行流在遍歷時可能是無序的
方式二: Arrays類的靜態方法 stream() 將數組轉換爲流
public static <T> Stream<T> stream(T[] array)
示例:
String [] strArray = new String[] {"a", "b", "c","d"};
Stream<String> arrayToStream = Arrays.stream(strArray);
方式三:Stream接口靜態方法 of() 初始化流
public static<T> Stream<T> of(T... values)
示例:
Stream<String> stream = Stream.of("a","b","c","d");
方式四:Stream接口靜態方法 iterate()、generat()創建無限流
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
public static<T> Stream<T> generate(Supplier<T> s)
示例:
//0 2
Stream<Integer> iterateStream = Stream.iterate(0, (x) -> x+2).limit(2);
//0.7959655194696649 0.04986900509440961 0.4258300512930294 0.6815133817071739
Stream<Double> generateStrem = Stream.generate(Math::random).limit(4);
流的常用操作:
- 1.篩選/過濾:filter/distinct/limit/skip
方法 |
描述 |
---|
filter(Predicate p) |
接收 Lambda , 從流中排除某些元素 |
distinct() |
篩選,通過流所生成元素的 hashCode() 和 equals() 去 除重複元素 |
limit(long maxSize) |
截斷流,使其元素不超過給定數量 |
skip(long n) |
跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素 不足 n 個,則返回一個空流。與 limit(n) 互補 |
示例:
Stream<String> strStream = Stream.of("pugb","king","lol","ow","solo","ow","");
strStream
.filter(s -> s.length()>0)//過濾字符串長度小於0的元素
.distinct() //元素去重 (需要重寫 hashcode和 equals方法)
.skip(2) //跳過流的前兩個元素
.limit(2) //從第三個元素起取2個元素
.forEach(System.out::println);//內部迭代流中的元素
執行結果:lol ow
map(Function f) |
接收一個函數作爲參數,該函數會被應用到每個元 素上,並將其映射成一個新的元素 |
mapToDouble(ToDoubleFunction f) |
接收一個函數作爲參數,該函數會被應用到每個元 素上,產生一個新的 DoubleStream |
mapToInt(ToIntFunction f) |
接收一個函數作爲參數,該函數會被應用到每個元 素上,產生一個新的 IntStream |
mapToLong(ToLongFunction f) |
接收一個函數作爲參數,該函數會被應用到每個元 素上,產生一個新的 LongStream |
flatMap(Function f) |
接收一個函數作爲參數,將流中的每個值都換成另一個流,然後把所有流連接成一個流 |
示例:
List<String> list = Arrays.asList("a","ab","abc","abcd","dh");
list.stream()
.map(String::toUpperCase)//將元素轉爲大寫
.forEach(System.out::println);//內部迭代流中的元素
System.out.println("---------------------------------------")
String[] arrayOfWords = {"Hello", "World"};
List<String> words = Arrays.asList(arrayOfWords);
words.stream()
.map(w -> split(""))
.flatMap(Arrays::stream)
.distinct()
.forEach(System.out::println);
執行結果:
A AB ABC ABCD DH
--------------------------------------------------
H
l
o
W
r
d
方法 |
描述 |
---|
sorted() |
產生一個新流,其中按自然順序排序 |
sorted(Comparator comp) |
產生一個新流,其中按比較器順序排序 |
示例:
List<Integer> sortList = Arrays.asList(1,2,13,4,15,6,17,8,19);
sortList.stream()
.sorted()//排序
.forEach(System.out::println);//內部迭代流中的元素
sortList.stream()
.sorted(Integer::compare)//排序
.forEach(System.out::println);//內部迭代流中的元素
執行結果:1 2 4 6 8 13 15 17 19
方法 |
描述 |
---|
allMatch(Predicate p) |
檢查是否匹配所有元素 |
anyMatch(Predicate p) |
檢查是否至少匹配一個元素 |
noneMatch(Predicate p) |
檢查是否沒有匹配所有元素 |
findFirst() |
返回第一個元素 |
findAny() |
返回當前流中的任意元素 |
count() |
返回流中元素總數 |
max(Comparator c) |
返回流中最大值 |
min(Comparator c) |
返回流中最小值 |
forEach(Consumer c) |
內部迭代(使用 Collection 接口需要用戶去做迭 代,稱爲外部迭代。相反,Stream API 使用內部 迭代——它幫你把迭代做了) |
示例:
List<String> matchList = Arrays.asList("pugb","king","lol","ow","solo","ow","");
boolean anyMatch = matchList.stream().anyMatch(str -> str.equals("king"));
boolean noneMatch = matchList.stream().noneMatch(String::isEmpty);
boolean allMatch = matchList.stream().allMatch(String::isEmpty);
System.out.println(anyMatch); //true
System.out.println(noneMatch); //false
System.out.println(allMatch); //false
Optional<String> findFirst = matchList.stream().findFirst();
System.out.println(findFirst.get()); //pugb
Optional<String> findAny = matchList.stream().filter(s -> s.equals("king")).findAny();
System.out.println(findAny.get()); //king
示例:
List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics();
System.out.println("列表中最大的數 : " + stats.getMax()); //19
System.out.println("列表中最小的數 : " + stats.getMin()); //1
System.out.println("所有數之和 : " + stats.getSum()); //85
System.out.println("平均數 : " + stats.getAverage()); // 9.444444444444445
System.out.println("=========================================================");
OptionalInt max = integers.stream().mapToInt((x) ->x).max();
OptionalInt min = integers.stream().mapToInt((x) ->x).min();
int sum = integers.stream().mapToInt((x) ->x).sum();
OptionalDouble avg = integers.stream().mapToInt((x) ->x).average();
System.out.println("最大值: " +max.getAsInt()); //19
System.out.println("最小值: " +min.getAsInt()); //1
System.out.println(" 和:" +sum); //85
System.out.println("平均值: " +avg.getAsDouble()); // 9.444444444444445
System.out.println("=========================================================");
//Stream中只有max和min兩個方法 sum/avg需轉換爲對應的包裝類Stream計算
Optional<Integer> maxInteger = integers.stream().max(Integer::compare);
Optional<Integer> minInteger = integers.stream().min(Integer::compare);
System.out.println(maxInteger.get()); //19
System.out.println(minInteger.get()); //1
方法 |
描述 |
---|
reduce(T iden, BinaryOperator b) |
可以將流中元素反覆結合起來,得到一個值。 返回 T |
reduce(BinaryOperator b) |
可以將流中元素反覆結合起來,得到一個值。 返回 Optional< T> |
備註:map 和 reduce 的連接通常稱爲 map-reduce 模式,因 Google 用它 來進行網絡搜索而出名
示例:
//求和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum); //55
準備Employee類:重寫hashCode和equals方法
public class Employee {
private Integer id;
private String name;
private Integer age;
private Double salary;
private Integer status;
//省略getter/setter/toString/hashCode/equals方法
}
定義集合:
List<Employee> emps = Arrays.asList(
new Employee(101, "林青霞", 28, 9889.99),
new Employee(102, "東方不敗", 29, 4329.85),
new Employee(103, "周星馳", 40, 1233.88),
new Employee(104, "大聖", 500, 5000.44),
new Employee(105, "張無忌", 15, 3000.09),
new Employee(102, "東方不敗", 29, 4329.85)
);
收集示例:
emps.stream()
.map(Employee::getName)//將員工姓名轉爲stream
.collect(Collectors.toList())//將員工姓名stream轉爲List集合
.forEach(System.out::println);
emps.stream()
.map(Employee::getName)//將員工姓名轉爲stream
.collect(Collectors.toSet())//將員工姓名stream轉爲Set集合
.forEach(System.out::println);
emps.stream()
.map(Employee::getName)//將員工姓名轉爲stream
.collect(Collectors.toCollection(HashSet::new))//將員工姓名stream轉爲HashSet集合
.forEach(System.out::println);
執行結果:
林青霞 東方不敗 周星馳 大聖 張無忌 東方不敗
-------------------
周星馳 林青霞 大聖 東方不敗 張無忌
-------------------
周星馳 林青霞 大聖 東方不敗 張無忌
統計示例:
Long count = emps.stream().collect(Collectors.counting());
System.out.println("員工個數:" + count);
Double avgSalary = emps.stream().
collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println("平均工資:" + avgSalary);
Double sumSalary= emps.stream().
collect(Collectors.summingDouble(Employee::getSalary));
System.out.println("總工資:" + sumSalary);
Optional<Employee> maxSalary = emps.stream()
.collect(Collectors.maxBy((x, y) -> Double.compare(x.getSalary(), y.getSalary())));
System.out.println("最高工資員工:" + maxSalary.get());
Optional<Double> minSalary = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println("最低工資:" + minSalary.get());
//統計分析
DoubleSummaryStatistics doubleSummaryStatistics = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println("平均工資:" + doubleSummaryStatistics.getAverage());
System.out.println("最高工資:" + doubleSummaryStatistics.getMax());
System.out.println("最低工資:" + doubleSummaryStatistics.getMin());
System.out.println("總工資:" + doubleSummaryStatistics.getSum());
//拼接
String join = emps.stream()
.map(Employee::getName)//將員工姓名轉爲stream
.collect(Collectors.joining(",", "--", "--"));//拼接符"," 前綴"--" 後綴"--"
System.out.println(join);
執行結果:
員工個數:6
平均工資:4630.683333333333
總工資:27784.1
最高工資員工:Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]
最低工資:1233.88
平均工資:4630.683333333333
最高工資:9889.99
最低工資:1233.88
總工資:27784.1
--林青霞,東方不敗,周星馳,大聖,張無忌,東方不敗--
收集分組:
Map<String, List<Employee>> group = emps.stream()
.collect(Collectors.groupingBy(Employee::getName));
System.out.println(group);
執行結果:
{
周星馳=[Employee [id=103, name=周星馳, age=40, salary=1233.88, status=null]],
林青霞=[Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]],
大聖=[Employee [id=104, name=大聖, age=500, salary=5000.44, status=null]],
東方不敗=[Employee [id=102, name=東方不敗, age=29, salary=4329.85, status=null],
Employee [id=102, name=東方不敗, age=29, salary=4329.85, status=null]
],
張無忌=[Employee [id=105, name=張無忌, age=15, salary=3000.09, status=null]]}
多級分組:
Map<String, Map<String, List<Employee>>> moreGroup = emps.stream()
.collect(Collectors.groupingBy(Employee::getName, Collectors.groupingBy((e) -> {
if (e.getAge() < 30) return "青年";
else if (e.getAge() < 50) return "中年";
else return "老年";
})));
System.out.println(moreGroup);
執行結果:
{
周星馳={中年=[Employee [id=103, name=周星馳, age=40, salary=1233.88, status=null]]},
林青霞={青年=[Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]]},
大聖={老年=[Employee [id=104, name=大聖, age=500, salary=5000.44, status=null]]},
東方不敗={青年=[Employee [id=102, name=東方不敗, age=29, salary=4329.85, status=null],
Employee [id=102, name=東方不敗, age=29, salary=4329.85, status=null]]
},
張無忌={青年=[Employee [id=105, name=張無忌, age=15, salary=3000.09, status=null]]}}
Collector 接口中方法的實現決定了如何對流執行收集操作(如收 集到 List、Set、Map)。但是 Collectors 實用類 供了很多靜態 方法,可以方便地創建常見收集器實例,具體方法與實例如下表:
方法 |
返回類型 |
作用 |
---|
toList |
List<T> |
把流中元素收集到List |
---|
List<Employee> emps= list.stream().collect(Collectors.toList()); |
---|
toSet |
Set<T> |
把流中元素收集到Set |
---|
Set<Employee> emps= list.stream().collect(Collectors.toSet()); |
---|
toCollection |
Collection<T> |
把流中元素收集到創建的集合 |
---|
Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new)); |
---|
counting |
Long |
計算流中元素的個數 |
---|
long count = list.stream().collect(Collectors.counting()); |
---|
summingInt |
Integer |
對流中元素的整數屬性求和 |
---|
inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary)); |
---|
averagingInt |
Double |
計算流中元素Integer屬性的平均 值 |
---|
doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary)); |
---|
summarizingInt |
IntSummaryStatistics |
收集流中Integer屬性的統計值。 如:平均值 |
---|
IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); |
---|
joining |
String |
連接流中每個字符串 |
---|
String str= list.stream().map(Employee::getName).collect(Collectors.joining()); |
---|
maxBy |
Optional<T> |
根據比較器選擇最大值 |
---|
Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary))); |
---|
minBy |
Optional<T> |
根據比較器選擇最小值 |
---|
Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary))); |
---|
reducing |
歸約產生的類型 |
從一個作爲累加器的初始值 開始,利用BinaryOperator與 流中元素逐個結合,從而歸 約成單個值 |
---|
inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum)); |
---|
collectingAndThen |
轉換函數返回的類型 |
包裹另一個收集器,對其結 果轉換函數 |
---|
inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); |
---|
groupingBy |
Map<K, List<T>> |
根據某屬性值對流分組,屬 性爲K,結果爲V |
---|
Map<Emp.Status, List<Emp>> map= list.stream() .collect(Collectors.groupingBy(Employee::getStatus)); |
---|
partitioningBy |
Map<Boolean,List<T>> |
根據true或false進行分區 |
---|
Map<Boolean,List<Emp>>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage)); |
---|