Stream
1. Stream流
1.1. Stream流引入
Stream流完全不是I/O流,按照流水線處理方式來考慮代碼中的思想。
JDK1.8 之後,我們擁有了Lambda表達式,讓代碼的中心偏向解決實際問題,直到重點,可以提高效率。
Stream流中使用了大量Lambda表達式,利用Lambda操作方式,提供開發效率
1.2 傳統遍歷方式和Stream類處理方式對比
package com.qfedu.a_stream;
import java.util.ArrayList;
/**
* 操作集合處理集合
* 操作過程中創建了多個ArrayList,但是這些ArrayList都是過客,
* 不涉及到程序的核心,這裏會導致資源的浪費。
* @author Anonymous 2020/3/12 9:56
*/
public class Demo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("宮保雞丁");
list.add("醬牛肉");
list.add("羊肉串");
list.add("烤羊排");
list.add("羊肉湯");
list.add("驢肉火燒");
// 第一步: 對List進行過程,找出list中保存元素帶有肉的,保存到另一個集合中
ArrayList<String> list1 = new ArrayList<>();
for (String s : list) {
if (s.contains("肉")) {
list1.add(s);
}
}
// 第二步: 找出菜名長度==3的,保存到另一個集合中
ArrayList<String> list2 = new ArrayList<>();
for (String s : list1) {
if (3 == s.length()) {
list2.add(s);
}
}
// 第三步:遍歷展示對應的數據
for (String s : list2) {
System.out.println(s);
}
}
}
package com.qfedu.a_stream;
import java.util.ArrayList;
/**
* 操作集合處理集合
* 採用Stream流方式
*
* @author Anonymous 2020/3/12 9:56
*/
public class Demo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("宮保雞丁");
list.add("醬牛肉");
list.add("羊肉串");
list.add("烤羊排");
list.add("羊肉湯");
list.add("驢肉火燒");
/*
採用Stream流方式來處理
直觀角度可以發現,這裏Stream流處理方式可以簡化代碼邏輯,
更側重於操作的重點
第一步: 對List進行過程,找出list中保存元素帶有肉的,
第二步: 找出菜名長度==3的
第三步:遍歷展示對應的數據
*/
list.stream()
.filter(s->s.contains("肉"))
.filter(s -> 3 == s.length())
.forEach(s -> System.out.println(s));
}
}
1.3 Stream流對應的思想
請你忘掉原先的IO流思想
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FQC4twIB-1584855460869)(img/流水線引入Stream思想.png)]
流水線:
原材料從頭到尾只會佔用一份空間,中間的過程中不會佔用空間。最後生成一個結果。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-uArJZR7s-1584855460870)(img/Stream流模型圖例.png)]
Stream流有一些特徵:
1. 帶有很多Stream流操作的方法, filter,limit,map,sorted,skip...這些方法大多是都會使用到函數式接口,那就意味着有lambda表達式
2. 整個Stream流模型操作過程中,只有執行到count,foreach這些方法,操作真正的執行中的模型,如果不存在結果導向,中間的所有操作是無效的,這裏得益於Lambda表達式的延後性
3. Stream流是存在一定的管道性 Pipelining 流水線
1.4 獲取Stream流
java.util.stream.Stream<T> JDK1.8的新特徵
1. 所有的Collection<T>集合都有對應的Stream();
2. 可以通過Stream類中的static Stream of()獲取
static Stream<T> of(T... t);
static Stream<T> of(T t);
package com.qfedu.b_streamget;
import java.util.*;
import java.util.stream.Stream;
/**
* Stream流獲取方式
* 1. Collection集合
* 2. Map雙邊隊列
* 3. 數組
*
* @author Anonymous 2020/3/12 11:16
*/
public class Demo1 {
public static void main(String[] args) {
// List接口獲取對應的Stream流類對象
List<String> list = new ArrayList<>();
// 根據list保存元素的類型來約束對應的Stream流對象操作所需類型
Stream<String> stream = list.stream();
// 通過Set集合獲取對應Stream流對象
HashSet<String> set1 = new HashSet<>();
Stream<String> stream1 = set1.stream();
HashMap<String, String> map = new HashMap<>();
// Map雙邊隊列中所有鍵對應的Set集合
Set<String> keySet = map.keySet();
Stream<String> stream2 = keySet.stream();
// Map雙邊隊列中所有value對應的Collection集合
Collection<String> values = map.values();
Stream<String> stream3 = values.stream();
// map中有一個方法,可以獲取所有鍵值對類型的Set集合
// Entry ==> 鍵值對,是Map接口的一個成員接口
Set<Map.Entry<String, String>> entrySet = map.entrySet();
// 獲取Map鍵值對集合的Stream流對象
Stream<Map.Entry<String, String>> stream4 = entrySet.stream();
// 使用不定長參數獲取對應的Stream流對象
// Stream類內的靜態方法of,更多的是用於數組操作提供Stream流對象
Stream<String> stringStream = Stream.of("醬牛肉", "羊肉抓飯", "羊肉湯", "羊蠍子");
String[] arr = {"黃河大鯉魚", "方中山胡辣湯", "蕭記燴麪", "蔡記蒸餃", "葛記燜餅"};
Stream<String> arrStream = Stream.of(arr);
// 不建議這樣使用!!!
ArrayList<String> list1 = new ArrayList<>();
Stream<ArrayList<String>> list11 = Stream.of(list1);
}
}
1.5 Stream常用方法
延遲方法:
返回值類型依然是Stream接口本身,並沒有影響我們操作真正的資源
允許鏈式操作,
例如
filter(XXX).limit(XXX).sorted(XXX).
終結方法:
返回值類型不是Stream接口本身,要麼處理數據,要麼返回其他類型數據,並且不再支持Stream流對象鏈式操作,count,foreach
1.5.1 foreach方法【終結方法】
void foreach(Consumer<? super T> action);
/*
終結方法:
需要一個Consumer接口進行操作處理,消耗一個數據
Consumer接口是一個【函數式接口】那就可以使用Lambda表達式
Consumer接口中方法是
void accept(T t);
*/
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* Stream流foreach方法使用
*
* @author Anonymous 2020/3/12 11:39
*/
public class Demo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("蛋炒飯");
list.add("虎皮青椒");
list.add("手撕包菜");
list.add("鯡魚罐頭");
// 得到List對應的Stream流對象
Stream<String> stream = list.stream();
/*
Stream<String> Stream流,這裏操作的是String類型
匿名內部類的匿名對象作爲方法的參數,曾經多麼厲害,現在是有點low
*/
stream.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
// 這裏需要的參數是Consumer函數式接口,完成可以使用Lambda表達式完成
stream.forEach(string -> System.out.println(string));
// 我們在哪裏見過??? 方法引用
stream.forEach(System.out::println);
}
}
1.5.2 filter方法
Stream<T> filter(Predicate<? super T> condition);
/*
filter是過濾方式,需要的參數是Predicate接口,Predicate是一個函數式接口,可以直接使用Lambda表達運行。
這裏返回值類型是Stream類對象,是經過過濾之後的Stream類型,可以進行鏈式操作
Predicate接口中需要實現的方法
boolean test(T t);
*/
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* Stream流對象filter方法演示
*
* @author Anonymous 2020/3/12 11:50
*/
public class Demo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("擀麪皮");
list.add("肉夾饃");
list.add("冰峯");
list.add("水盆羊肉");
Stream<String> stringStream = list.stream().filter(s -> s.length() >= 3);
stringStream.forEach(System.out::println);
Stream<String> stringStream1 = stringStream.filter(s -> 4 == s.length());
stringStream1.forEach(System.out::println);
/*
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)
at java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94)
at java.util.stream.ReferencePipeline$StatelessOp.<init>(ReferencePipeline.java:618)
at java.util.stream.ReferencePipeline$2.<init>(ReferencePipeline.java:163)
at java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:162)
at com.qfedu.c_streamfunction.Demo2.main(Demo2.java:23)
*/
}
}
stream has already been operated upon or closed
爲何會出現這個錯誤?
因爲調用終結方法後,Stream流已經被銷燬,所以不能再對Stream流進行操作。
1.5.3 map方法
<R> Stream<R> map(Function<? super T, ? super R> fun);
/*
類型轉換操作,得到的一個轉換之後數據類型的Stream流對象
這裏需要的參數是Function函數式接口,
R apply(T t);
T類型的數據轉換成R類型數據
*/
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* Stream流 map映射方法演示
*
* @author Anonymous 2020/3/12 14:39
*/
public class Demo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("1,騷磊,16");
list.add("2,騷傑,66");
list.add("3,老黑,46");
list.add("4,老付,36");
list.add("5,污雲,56");
list.add("6,帥棟,26");
System.out.println(list);
Stream<Person> personStream = list.stream().map(s -> {
String[] split = s.split(",");
Person person = new Person();
person.setId(Integer.parseInt(split[0]));
person.setName(split[1]);
person.setAge(Integer.parseInt(split[2]));
return person;
});
personStream.forEach(System.out::println);
}
}
1.5.4 count方法【終結方法】
long count();
/*
返回當前Stream流對象中有多少個元素
類似有Collection接口下的size(). String的length();
【終結方法】
一旦執行Stream流對象被關閉
*/
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
/**
* Stream流對象 count方法演示【終結方法】
*
* @author Anonymous 2020/3/12 14:50
*/
public class Demo4 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("擀麪皮");
list.add("肉夾饃");
list.add("冰峯");
list.add("水盆羊肉");
// 當前Stream流對象中有多少個元素,終結方法
long count = list.stream().filter(s -> s.length() >= 3).count();
System.out.println(count);
}
}
1.5.5 limit方法
Stream<T> limit(long maxSize);
/*
對於當前Stream流對象操作的數據進行限制操作,限制個數到maxSize
例如:
Stream流中保存的有10個元素,limit 5 ==> 前五個元素
*/
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* Stream流對象 limit方法
*
* @author Anonymous 2020/3/12 15:03
*/
public class Demo5 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("紅旗");
list.add("領克");
list.add("吉利");
list.add("比亞迪");
list.add("長安");
list.add("五菱宏光");
Stream<String> stream = list.stream();
// @throws IllegalArgumentException if {@code maxSize} is negative
stream.limit(5).forEach(System.out::println);
}
}
1.5.6 skip方法
Stream<T> skip(long n);
/*
返回值依然是一個Stream流對象,這裏跳過當前Stream流對象前n個元素
*/
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
/**
* Stream流對象 skip方法
*
* @author Anonymous 2020/3/12 15:10
*/
public class Demo6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("陸巡");
list.add("高R");
list.add("RS7");
list.add("s4");
list.add("霸道");
list.add("道奇");
list.stream().skip(2).forEach(System.out::println);
}
}
1.5.7 concat方法
static Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
/*
拼接兩個Stream流對象,是一個靜態方法,得到新的Stream流對象
*/
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* Stream流 靜態成員方法 concat方法
*
* @author Anonymous 2020/3/12 15:15
*/
public class Demo7 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("紅旗");
list.add("領克");
list.add("吉利");
list.add("比亞迪");
list.add("長安");
list.add("五菱宏光");
ArrayList<String> list1 = new ArrayList<>();
list1.add("陸巡");
list1.add("高R");
list1.add("RS7");
list1.add("s4");
list1.add("霸道");
list1.add("道奇");
// 獲取到兩個Stream流,其中保存的數據類型都是String類型
Stream<String> stream = list.stream();
Stream<String> stream1 = list1.stream();
Stream<String> concat = Stream.concat(stream, stream1);
concat.forEach(System.out::println);
}
}
1.5.8 原始操作方式和Stream流方式對比
1. 一個String類型的字符串集合,"1,騷磊,16"
2. 過濾沒有5的數據
3. 跳過前三個數據
4. 限制得到前5個數據
5. 兩個String類型集合字符串合併
6. 轉換成Person類型
7. 展示數據
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
/**
* 採用原始的List集合方式操作
* 1. 一個String類型的字符串集合,"1,騷磊,16"
* 2. 過濾沒有5的數據
* 3. 跳過前三個數據
* 4. 限制得到前5個數據
* 5. 兩個String類型集合字符串合併
* 6. 轉換成Person類型
* 7. 展示數據
*
* 太浪費資源,浪費時間!!!
* @author Anonymous 2020/3/12 15:22
*/
public class Demo8 {
public static void main(String[] args) {
// 1. 一個String類型的字符串集合,"1,騷磊,16"
ArrayList<String> list1 = new ArrayList<>();
list1.add("1,騷磊,15");
list1.add("2,騷傑,65");
list1.add("3,老黑,45");
list1.add("4,老付,56");
list1.add("5,污雲,56");
list1.add("6,帥棟,26");
list1.add("7,帥棟,56");
list1.add("8,帥棟,25");
list1.add("9,帥棟,26");
list1.add("10,帥棟,26");
list1.add("11,帥棟,25");
list1.add("12,帥棟,56");
list1.add("13,帥棟,55");
// 2. 過濾沒有5的數據
ArrayList<String> list2 = new ArrayList<>();
for (String s : list1) {
if (s.contains("5")) {
list2.add(s);
}
}
System.out.println(list2);
// 3. 跳過前三個數據
ArrayList<String> list3 = new ArrayList<>();
for (int i = 3; i < list2.size(); i++) {
list3.add(list2.get(i));
}
System.out.println(list3);
// 4. 限制得到前5個數據
ArrayList<String> list4 = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list4.add(list3.get(i));
}
// 5. 兩個String類型集合字符串合併
ArrayList<String> list5 = new ArrayList<>();
list5.add("1,騷磊,15");
list5.add("2,騷傑,65");
list5.add("3,老黑,45");
list5.add("4,老付,56");
list5.add("5,污雲,56");
list4.addAll(list5);
// 6. 轉換成Person類型
ArrayList<Person> list6 = new ArrayList<>();
for (String s : list4) {
String[] split = s.split(",");
Person person = new Person();
person.setId(Integer.parseInt(split[0]));
person.setName(split[1]);
person.setAge(Integer.parseInt(split[2]));
list6.add(person);
}
// 7. 展示數據
for (Person person : list6) {
System.out.println(person);
}
}
}
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* 採用原始的Stream流對象思想方式操作
* 1. 一個String類型的字符串集合,"1,騷磊,16"
* 2. 過濾沒有5的數據
* 3. 跳過前三個數據
* 4. 限制得到前5個數據
* 5. 兩個String類型集合字符串合併
* 6. 轉換成Person類型
* 7. 展示數據
* @author Anonymous 2020/3/12 15:29
*/
public class Demo9 {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
list1.add("1,騷磊,15");
list1.add("2,騷傑,65");
list1.add("3,老黑,45");
list1.add("4,老付,56");
list1.add("5,污雲,56");
list1.add("6,帥棟,26");
list1.add("7,帥棟,56");
list1.add("8,帥棟,25");
list1.add("9,帥棟,26");
list1.add("10,帥棟,26");
list1.add("11,帥棟,25");
list1.add("12,帥棟,56");
list1.add("13,帥棟,55");
ArrayList<String> list2 = new ArrayList<>();
list2.add("1,騷磊,15");
list2.add("2,騷傑,65");
list2.add("3,老黑,45");
list2.add("4,老付,56");
list2.add("5,污雲,56");
list2.add("6,帥棟,26");
list2.add("7,帥棟,56");
Stream<String> stream = list1.stream();
Stream<String> limit = stream.filter(s -> s.contains("5")).
skip(3).
limit(5);
Stream.concat(limit, list2.stream()).map(s -> {
String[] split = s.split(",");
Person person = new Person();
person.setId(Integer.parseInt(split[0]));
person.setName(split[1]);
person.setAge(Integer.parseInt(split[2]));
return person;
}).forEach(System.out::println);
}
}