Stream API ( Java 8 )
阿新 • • 發佈:2021-01-16
Stream API 簡介
Stream’API是Java8中的新特性,基於Lambda表示式,對Collection(集合)的各種操作有了很大的改變,極大的提升了編碼效率和程式碼的可讀性。Stream有序列和並行兩種模式,並行模式會自動建立多個執行緒,使用fork(join)Java7特性,來拆分任務和加速處理過程。Stream是一種類似IO流的東西,但是並不相同,實質是對集合操作的一種高度抽象,而且更重要的是,Stream不是資料結構,是不儲存資料的,資料儲存在底層的集合中,或者根據需要產生出來(例如Stream進行終端操作的時候,生成新的集合)。
Stream抽象概念
Stream將要處理的資料看作一個流,流在管道中流動,可以在管道的節點上對流進行篩選,排序,聚合等操作,也就是資料流在管道中經過中間操作(intermediate operation),最後由終端操作(terminal operation)來得到前面處理過的資料,可以抽象的看作一種類似通過sql查詢資料的方式。
Stream的特徵
- Stream不是資料結構,是不儲存資料的;
- Stream的構成資料來源是集合、陣列,I/O channel,generate等等;
- Stream的中間操作會返還流物件本身,這樣就形成了一個管道;
- Stream的中間操作類似sql語句,比如filter(篩選), map(元素對映), reduce(聚合), find(查詢), match(匹配), sorted(排序)等;
- Stream的迭代跟for和foreach在集合外顯式迭代不同,Stream是內部迭代,基於訪問者模式(Visitor)實現;
- Stream有序列和並行兩種模式;
Stream用法
如何建立Stream流
- stream() 為集合建立序列流。
- parallelStream() 為集合建立並行流。
// 程式碼示例
// 集合建立流 Collection介面增加了Steam方法()
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript" );
Stream<String> steamStr = listStr.stream();
steamStr.forEach(str -> System.out.println(str));
Set<String> setStr = ZoneId.getAvailableZoneIds();
Stream<String> steamSetStr = setStr.stream();
steamSetStr.forEach(str -> System.out.println(str));
// 從陣列建立流 Arrays類中增加stream(T[] array)方法
String[] strs = {"java", "python", "shell", "javaScript"};
Stream<String> stringStream = Arrays.stream(strs);
// 從靜態方法中建立
Stream<String> staticStream1 = Stream.of("java", "python", "shell", "javaScript");
staticStream1.forEach(str -> System.out.println(str));
// 使用iterate靜態方法(迭代器的方式)建立無限大小的流,需配合limit使用,防止記憶體溢位
// 會一直增加資料,沒有上限
Stream<Integer> staticStream2 = Stream.iterate(0, x -> x + 1).limit(10);
staticStream2.forEach(integer -> System.out.println(integer));
// 使用generate靜態方法建立無限大小的流,需要配合limit使用,防止記憶體溢位
// generate方式建立的無限流最大值是Long.MAX_VALUE
Random random = new Random();
Stream<Integer> staticStream3 = Stream.generate(() -> random.nextInt()).limit(10);
staticStream3.forEach(integer -> System.out.println(integer));
// 其他方式
java.io.BufferedReader.lines()
java.util.stream.IntStream.range()
java.nio.file.Files.walk()
java.util.Spliterator
Random.ints()
BitSet.stream()
Pattern.splitAsStream(java.lang.CharSequence)
JarFile.stream()
Stream流的中間操作
該表的作用是Stream流中間操作方法的引數型別
函式式介面名 | 作用 |
---|---|
Function<T, R> | 接受一個引數T,返回結果R |
Predicate | 接受一個引數T,返回boolean |
Supplier | 不接受任何引數T,返回結果T |
Consumer | 接受一個引數T,不返回結果 |
UnaryOperator | 繼承自Function<T,T>,返回相同型別T的結果 |
BiFunction<T, U, R> | 接收兩個引數T,U,返回結果R |
BinaryOperator | 繼承自BiFunction<T,T,T>,返回相同型別T的結果 |
Runnable | 實際上不接受任何引數,也不返回結果 |
Comparable | 實際上是接受兩個相同型別的T,返回int |
Callable | 不接受任何引數,返回結果V |
過濾操作
- filter() 將符合條件的所有元素(資料)轉移到新流中。filter的引數型別是Predicate
// 程式碼示例
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript");
Stream<String> steamStr = listStr.stream();
steamStr.filter(s -> s.startsWith("j")).forEach(s -> System.out.println(s));
轉換操作
- map() 將所有資料經過處理之後(可以改變物件型別)轉移到新流中。map的引數型別是Function<T,R>
- flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) 接收一個泛型引數,另一個為Stream流引數,返回的是泛型R,作用是將兩個流合併成一個流輸出
// 程式碼示例
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript");
Stream<String> steamStr = listStr.stream();
steamStr.map(s -> s.substring(2)).forEach(s -> System.out.println(s));
String[] strs = { "aaa", "bbb", "ccc" };
Arrays.stream(strs).map(str -> str.split("")).forEach(System.out::println);
// [Ljava.lang.String;@5cc7c2a6
// [Ljava.lang.String;@b97c004
// [Ljava.lang.String;@4590c9c3
Arrays.stream(strs).map(str -> str.split("")).flatMap(str -> Arrays.stream(str)).forEach(System.out::println);
// a/a/a/b/b/b/c/c/c
// map操作將strs拆分為三個陣列,stream流中的元素由stream<String>變成了stream<String[]>
// flatMap操作將三個stream<String[]>流合併成一個stream<String>流
提取操作
- skip(long n) 忽略流中的前n個元素(資料)
- limit(long maxSize) 獲取流中的maxSize個元素(資料)
// 程式碼示例
List<String> listStr = Arrays.asList("java", "python", "shell", "javaScript");
Stream<String> steamStr = listStr.stream();
steamStr.skip(2).forEach(s -> System.out.println(s));
steamStr.limit(2).forEach(s -> System.out.println(s));
去重操作
- distinct() 去除流中重複的元素
// 程式碼示例
// 執行時記得註釋其中stringStream執行語句否則會有異常
// java.lang.IllegalStateException: stream has already been operated upon or closed
Stream<String> stringStream = Arrays.asList("java", "python", "shell", "javaScript", "java").stream();
stringStream.distinct().forEach(s -> System.out.println(s));
stringStream.forEach(s -> System.out.println(s));
排序操作
- sort() 將流中的元素(資料)排序
- sort(Comparator<? super T> comparator) 將流中元素根據屬性排序
// 程式碼示例
public class test {
public static List<Emp> emps = new ArrayList<>(10);
static {
emps.add(new Emp("1", "xiaoHong1", 20));
emps.add(new Emp("1", "xiaoHong2", 202));
emps.add(new Emp("1", "xiaoHong3", 32));
emps.add(new Emp("1", "xiaoHong4", 45));
emps.add(new Emp("1", "xiaoHong5", 17));
emps.add(new Emp("1", "xiaoHong6", 14));
emps.add(new Emp("1", "xiaoHong7", 65));
emps.add(new Emp("1", "xiaoHong8", 38));
}
public static class Emp {
private String code;
private String name;
private int age;
public Emp(String code, String name, int age) {
this.code = code;
this.name = name;
this.age = age;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
/**
* @param empList 輸出集合中的元素
*/
public static void println(List<Emp> empList) {
empList.stream().forEach(emp -> System.out.println(String.format("編號 %S , 姓名 %s , 年齡 %s", emp.getCode(), emp.getName(), emp.getAge())));
}
/**
* 根據物件中年齡進行排序
*/
public static List<Emp> sortByAge(List<Emp> empList) {
return empList.stream().sorted(Comparator.comparing(emp -> emp.getAge())).collect(Collectors.toList());
}
/**
* 取出物件中姓名排序,並只返回包含姓名的集合
*/
public static List<String> sortByName(List<Emp> empList) {
return empList.stream().map(emp -> emp.getName()).sorted().collect(Collectors.toList());
}
public static void main(String[] args) {
println(emps);
println(sortByAge(emps));
sortByName(emps).stream().forEach(s -> System.out.println(s));
}
}
操作物件
- peek(Consumer<? super T> action)
// 程式碼示例
/**
* @param empList 輸出集合中的元素
*/
public static void println(List<Emp> empList) {
empList.stream().forEach(emp -> System.out.println(String.format("編號 %S , 姓名 %s , 年齡 %s", emp.getCode(), emp.getName(), emp.getAge())));
}
/**
* 給年齡大於30歲增加十歲,並返回篩選後的集合
*/
public static List<Emp> addAge(List<Emp> empList) {
return empList.stream().filter(emp -> emp.getAge() > 30).peek(emp -> emp.setAge(emp.getAge() + 10)).collect(Collectors.toList());
}
public static void main(String[] args) {
println(addAge(emps));
}
聚合操作
- reduce方法
- reduce(BinaryOperator<T> accumulator)
接收一個引數,且引數型別為函式式介面的計算規則,初始值是List集合的第一個值
- reduce(T identity, BinaryOperator<T> accumulator)
引數型別為函式式介面的計算規則,第一引數值是初始值,第二個引數是函式式介面的計算規則
- reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
在多執行緒中使用的,具體不知道(https://segmentfault.com/q/1010000004944450)
// 程式碼示例
// 將流中元素歸約為一個值
Random random = new Random();
List<Integer> randomList = Stream.generate(() -> random.nextInt(10)).limit(10).collect(Collectors.toList());
randomList.stream().forEach(integer -> System.out.println(integer));
System.out.println(randomList.stream().reduce((a, b) -> a + b).get());
System.out.println(randomList.stream().reduce(1,(a, b) -> a + b).longValue());
List<Integer> iterateList = Stream.iterate(0, x -> x + 1).limit(10).collect(Collectors.toList());
iterateList.stream().forEach(integer -> System.out.println(integer));
System.out.println(iterateList.stream().reduce((a, b) -> a - b).get());
System.out.println(iterateList.stream().reduce(1,(a, b) -> a - b).longValue());
- collect方法
- collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);
傳入三個引數的抽象方法
- collect(Collector<? super T, A, R> collector);
該方法只有一個引數,這個引數先看下stream中的collect操作Collectors靜態工廠類,在這個靜態工廠類裡面,大多都使用三個引數的collect方法實現的。
具體的實現類方法
// 程式碼示例
public class test {
public static List<Emp> emps = new ArrayList<>(10);
static {
emps.add(new Emp("1", "xiaoHong1", 20));
emps.add(new Emp("1", "xiaoHong2", 202));
emps.add(new Emp("2", "xiaoHong3", 32));
emps.add(new Emp("1", "xiaoHong4", 45));
emps.add(new Emp("2", "xiaoHong5", 17));
emps.add(new Emp("1", "xiaoHong6", 14));
emps.add(new Emp("3", "xiaoHong7", 65));
emps.add(new Emp("3", "xiaoHong8", 38));
}
public static class Emp {
private String code;
private String name;
private int age;
public Emp(String code, String name, int age) {
this.code = code;
this.name = name;
this.age = age;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public static void main(String[] args) {
//轉List
List<String> nameList = emps.stream().map(emp -> emp.getName()).collect(Collectors.toList());
//轉Set
Set<String> nameSet = emps.stream().map(emp -> emp.getName()).collect(Collectors.toSet());
//轉map,需指定key值,Function.identity()指的是當前物件本身
Map<String, Emp> nameMap1 = emps.stream().collect(Collectors.toMap(emp -> emp.getName(), emp -> emp));
Map<String, Emp> nameMap2 = emps.stream().collect(Collectors.toMap(emp -> emp.getName(), Function.identity()));
//計算集合中元素個數
long count = emps.stream().collect(Collectors.counting());
//計算資料 summarizingInt summarizingDouble summarizingLong
IntSummaryStatistics sum = emps.stream().collect(Collectors.summarizingInt(emp -> emp.getAge()));
//平均數
System.out.println(sum.getAverage());
//總數
System.out.println(sum.getCount());
//最大值
System.out.println(sum.getMax());
//最小值
System.out.println(sum.getMin());
//求和
System.out.println(sum.getSum());
//連線字串 字首和字尾只會出現在整個字串的首尾
String nameStr1 = emps.stream().map(emp -> emp.getName()).collect(Collectors.joining());
String nameStr2 = emps.stream().map(emp -> emp.getName()).collect(Collectors.joining("中間-"));
String nameStr3 = emps.stream().map(emp -> emp.getName()).collect(Collectors.joining("中間-", "字首*", "字尾&"));
System.out.println(nameStr1);
System.out.println(nameStr2);
System.out.println(nameStr3);
//最大值 maxBy 最小值 minBy
Optional<Integer> maxAge = emps.stream().map(emp -> emp.getAge()).collect(Collectors.maxBy(Comparator.comparing(emp -> emp)));
Optional<Integer> minAge = emps.stream().map(emp -> emp.getAge()).collect(Collectors.minBy(Comparator.comparing(emp -> emp)));
System.out.println(maxAge.get());
System.out.println(minAge.get());
//聚合操作
Optional<Integer> ageSum1 = emps.stream().map(emp -> emp.getAge()).collect(Collectors.reducing((x, y) -> x + y));
Integer ageSum2 = emps.stream().map(emp -> emp.getAge()).collect(Collectors.reducing(1, (x, y) -> x + y));
System.out.println(ageSum1.get());
System.out.println(ageSum2);
//分組操作 根據地址把原List進行分組
Map<String, List<Emp>> mapGroup = emps.stream().collect(Collectors.groupingBy(emp -> emp.getCode()));
mapGroup.forEach((s, empList) -> mapGroup.get(s).forEach(emp -> System.out.println(s + ":" + emp.getName())));
//分割槽操作 需要根據型別進行 需要根據型別指定判斷分割槽
Map<Boolean, List<Emp>> mapPartitioning = emps.stream().collect(Collectors.partitioningBy(emp -> emp.getAge() > 20));
mapPartitioning.forEach((b, empList) -> mapPartitioning.get(b).forEach(emp -> System.out.println(b + ":" + emp.getName())));
List<String> listStr1 = Arrays.asList("java", "python", "shell", "javaScript");
List<String> listStr2 = Arrays.asList("javaNew", "pythonNew", "shellNew", "javaScriptNew");
Stream<String> steamSt1 = listStr1.stream();
Stream<String> steamSt2 = listStr2.stream();
Stream.concat(steamSt1, steamSt2).forEach(s -> System.out.println(s));
}
}
stream的終端操作
對集合的流進行遍歷
- forEach(Consumer<? super T> action)
- forEachOrdered(Consumer<? super T> action)
// 程式碼示例
// forEach遍歷是無序的遍歷
// forEach遍歷是按照元素的在流中的順序進行遍歷
List<String> strList = Arrays.asList("aaa","bbb","ccc");
strList.parallelStream().forEach(str-> System.out.println(str));
System.out.println();
strList.parallelStream().forEachOrdered(str-> System.out.println(str));
將流轉換為陣列
- toArray(IntFunction<A[]> generator)
- toArray()
// toArray()方法的底層呼叫的還是toArray(IntFunction<A[]> generator)方法
// 程式碼示例
List<String> strList = Arrays.asList("aaa", "bbb", "ccc");
Object[] o = strList.stream().toArray();
String[] s = strList.stream().toArray(str -> new String[strList.size()]);
//將集合轉換為陣列進行輸出
Arrays.stream(o).forEach(o1 -> System.out.println(o1));
Arrays.stream(s).forEach(s1 -> System.out.println(s1));
流的長度及流中元素(資料)比較
- long count() 計算流的長度
- boolean anyMatch(Predicate<? super T> predicate) 判斷條件中,流中任意一個元素符合,返回true
- boolean allMatch(Predicate<? super T> predicate) 判斷條件中,流中所有元素都符合,返回true
- boolean noneMatch(Predicate<? super T> predicate) 判斷條件中,流中任意一個元素都不符合,返回true
// 程式碼示例
public class test {
public static List<Emp> emps = new ArrayList<>(10);
static {
emps.add(new Emp("1", "xiaoHong1", 20));
emps.add(new Emp("1", "xiaoHong2", 202));
emps.add(new Emp("1", "xiaoHong3", 32));
emps.add(new Emp("1", "xiaoHong4", 45));
emps.add(new Emp("1", "xiaoHong5", 17));
emps.add(new Emp("1", "xiaoHong6", 14));
emps.add(new Emp("1", "xiaoHong7", 65));
emps.add(new Emp("1", "xiaoHong8", 38));
}
public static class Emp {
private String code;
private String name;
private int age;
public Emp(String code, String name, int age) {
this.code = code;
this.name = name;
this.age = age;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public static void main(String[] args) {
System.out.println(emps.stream().filter(emp -> emp.getAge() > 30).count());
// 5
System.out.println(emps.stream().allMatch(emp -> emp.getAge() > 30));
// false
System.out.println(emps.stream().anyMatch(emp -> emp.getName().startsWith("xiao")));
// true
System.out.println(emps.stream().noneMatch(emp -> emp.getAge() < 10));
// false
}
}
文章借鑑處
- https://blog.csdn.net/qq_28410283/article/details/80633710
- https://miaoxinguo.github.io/java/2016/06/02/java8.2.stream.html
- https://www.exception.site/java8/java8-stream-tutorial