java8新特性-Stream入門(mén)學(xué)習(xí)心得
上一篇介紹了Lambda的學(xué)習(xí),如果對(duì)Lambda表達(dá)式還不清晰的同學(xué)可以戳一下這個(gè)鏈接:java8新特性-lambda表達(dá)式入門(mén)學(xué)習(xí)。
java8除了提供了Lambda表達(dá)式,操作集合的Stream API也是非常新特性中最值得學(xué)習(xí)和掌握的,它大大簡(jiǎn)化了,我們操作數(shù)據(jù)集合的代碼量的書(shū)寫(xiě)。
簡(jiǎn)單來(lái)說(shuō)Stream是一個(gè)抽象概念,可以通過(guò)查找,過(guò)濾,映射等操作,這一點(diǎn)與Scala中集合操作很類(lèi)似。
Stream是什么?
通俗的說(shuō)就是操作數(shù)據(jù)集合的一種手段,你可以使用它,以獲取所需要的集合數(shù)據(jù)源類(lèi)型,如下圖所示:
通常Stream流操作整個(gè)流程是創(chuàng)建流對(duì)象->對(duì)流操作->獲得目標(biāo)數(shù)據(jù)源操作
創(chuàng)建Stream
通過(guò)Collection接口提供的Stream
1.返回一個(gè)順序流
default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); }
2.返回一個(gè)并行流
default Stream<E> parallelStream() { return StreamSupport.stream(spliterator(), true); }
那我們簡(jiǎn)單演示一下這兩個(gè)流的使用。
Emplyee的基本實(shí)體類(lèi)
package com.codegeek.lambda; import lombok.*; @Setter @Getter @NoArgsConstructor @ToString public class Employee { /** * 員工姓名 */ private String name; /** * 員工年齡 */ private int age; /** * 基本薪水 */ private double basicSalary; /** * 訂單成交總額 */ private double dealTotalPrice; public Employee(String name, int age, double basicSalary,double dealTotalPrice) { this.name = name; this.age = age; this.basicSalary = basicSalary; this.dealTotalPrice = dealTotalPrice; } /** * 員工總薪資=基本薪資+提成薪資 * * @return Double */ public Double getTotalSalary() { return this.basicSalary + this.dealTotalPrice * 0.04; } }
測(cè)試方法如下:
@Test public void test() { Employee qingLong = new Employee("青龍", 25, 5500, 7500); Employee baiHu = new Employee("白虎", 27, 5000, 9000); Employee zhuQue = new Employee("朱雀", 22, 3800, 4500); Employee xuanWu = new Employee("玄武", 24, 3300, 3300); List<Employee> employees = Arrays.asList(qingLong, baiHu, zhuQue, xuanWu); // 得到一個(gè)順序流,并獲取工資大與4000的員工的姓名 Stream<Employee> stream = employees.stream(); stream.filter(e-> e.getTotalSalary()>4000).map(Employee::getName).forEach(System.out::println); // 得到一個(gè)并行流,獲取年齡大于25的員工姓名 Stream<Employee> employeeStream = employees.parallelStream(); employeeStream.filter(employee -> employee.getAge()> 25).map(Employee::getName).forEach(System.out::println ); }
通過(guò)Arrays創(chuàng)建Stream流
注意數(shù)組里面是什么類(lèi)型的數(shù)組,就會(huì)產(chǎn)生同類(lèi)型的流。
1.產(chǎn)生IntStream
public static IntStream stream(int[] array) { ? ?return stream(array, 0, array.length); }
2.產(chǎn)生LongStream
public static LongStream stream(long[] array) { ?return stream(array, 0, array.length); }
3.產(chǎn)生DoubleStream
?public static DoubleStream stream(double[] array) { ? ?return stream(array, 0, array.length); }
下面簡(jiǎn)單演示以下IntStream的使用案列
// 初始化一個(gè)數(shù)組對(duì)象 ?int[] arr = {11, 55, 44, 20, 45, 16}; // 通過(guò)Arrays創(chuàng)建流對(duì)象是IntStream ?Arrays.stream(arr).sorted().forEach(System.out::println);
Stream常見(jiàn)的操作
上面簡(jiǎn)單介紹了下,流的創(chuàng)建,當(dāng)流創(chuàng)建好后,我們又該如何使用呢,常見(jiàn)流的操作如下?
- 過(guò)濾和切片
方法 | 方法介紹 |
---|---|
filter(Predicate<? super T> predicate) | 接收斷言接口,并從流中排除元素 |
distinct() | 去除流中重復(fù)的元素 |
limit(long maxSize) | 截取流中元素個(gè)數(shù),類(lèi)似sql查詢(xún)limit |
skip(long n) | 跳過(guò)元素,跳過(guò)前n個(gè)元素 |
使用演示:
@Test public void testFilter() { int[] age = {11, 22, 44, 22, 24, 24, 66, 77, 77, 25, 34}; // 使用filter過(guò)濾獲得大于33的數(shù)組元素 Arrays.stream(age).filter(i -> i > 33).forEach(System.out::println); // 去重 Arrays.stream(age).distinct().forEach(System.out::println); // 截取3個(gè)元素 Arrays.stream(age).limit(3).forEach(System.out::println); // 跳過(guò)前3個(gè)元素 Arrays.stream(age).skip(3).forEach(System.out::println); }
- 映射
方法 | 方法介紹 |
---|---|
map(Function mapper) | 接收一個(gè)函數(shù)式接口,將會(huì)映射到流中的每一個(gè)元素 |
mapToDouble(ToDoubleFunction mapper) | 接收函數(shù)式接口,將映射產(chǎn)生DoubleStream |
mapToLong(ToLongFunction mapper) | 接收函數(shù)式接口,將映射產(chǎn)生LongStream |
mapToInt(ToIntFunction mapper) | 接收函數(shù)式接口,將映射產(chǎn)生IntStream |
flatMap(Function extends Stream mapper) | 接收函數(shù),將流中的每個(gè)值都轉(zhuǎn)換一個(gè)流,然后將這些流匯成一個(gè)流 |
使用演示:
String[] arr = {"java", "scala", "php", "python", "c++"}; // 將流中的每一個(gè)元素轉(zhuǎn)換成大寫(xiě) Arrays.stream(arr).map(String::toUpperCase).forEach(System.out::println); //將流中的數(shù)據(jù)轉(zhuǎn)Double類(lèi)型 long[] array = {1, 4, 6, 7, 12}; // 返回Double類(lèi)型的Stream Arrays.stream(array).mapToDouble(e-> e* 100).forEach(System.out::println); // 返回Long類(lèi)型的Stream Arrays.stream(array).mapToLong(e -> e + 23).forEach(System.out::println); // flatMap演示 List<List<String>> database = new ArrayList<>(); List<String> noSql = Arrays.asList("redis", "hbase", "membercache"); List<String> sql = Arrays.asList("mysql", "oracle", "db2"); database.add(noSql); database.add(sql); List<String> h = database.stream().flatMap(s -> s.stream().filter(si -> si.contains("h"))).collect(Collectors.toList()); h.stream().forEach(System.out::println);
- 排序
- 排序相對(duì)簡(jiǎn)單,以之前定義的employee類(lèi)如下:
Employee qingLong = new Employee("青龍", 25, 5500, 7500); Employee baiHu = new Employee("白虎", 27, 5000, 9000); Employee zhuQue = new Employee("朱雀", 22, 3800, 4500); Employee xuanWu = new Employee("玄武", 24, 3300, 3300); List<Employee> employees = Arrays.asList(qingLong, baiHu, zhuQue, xuanWu); // 按照薪水的大小進(jìn)行排序 employees.stream().sorted(Comparator.comparing(Employee::getTotalSalary)).forEach(System.out::println);
- 查找與匹配
方法 | 方法介紹 |
---|---|
allMatch(Predicate p) | 檢查流中的元素是否都匹配 |
anyMatch(Predicate p) | 檢查是否匹配一個(gè)元素 |
noneMatch(Predicate p) | 檢查是否沒(méi)有匹配所有元素 |
findFirst() | 返回第一個(gè)元素 |
findAny() | 返回流中任意元素 |
count | 返回流中的個(gè)數(shù) |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
方法演示:以初始化的員工類(lèi)
// 判斷所有的員工年齡是否大于18 boolean b = employees.stream().allMatch(e -> e.getAge() > 18); System.out.println("allMatch="+b);// 結(jié)果為true // 判斷所有員工中有沒(méi)有年齡大于35的 boolean b1 = employees.stream().anyMatch(e -> e.getAge() > 35); System.out.println("anyMath=" + b1); // 結(jié)果為false // 判斷所有員工中沒(méi)有年齡大于35的 boolean b1 = employees.stream().noneMatch(e -> e.getAge() > 35); System.out.println("anyMath=" + b1); // 結(jié)果為true // 返回第一個(gè)員工的信息 Optional<Employee> first = employees.stream().findFirst(); System.out.println(first.get()); // 統(tǒng)計(jì)年齡大于20的員工個(gè)數(shù) ong count = employees.stream().filter(e -> e.getAge() > 20).count(); System.out.println("count="+count); // 統(tǒng)計(jì)集合中員工薪資最高的員工信息 Optional<Employee> max = employees.stream().max(Comparator.comparing(Employee::getTotalSalary)); System.out.println("max=" + max);
- 歸約
方法 | 方法介紹 |
---|---|
reduce(BinaryOperator p) | 將流中的元素反復(fù)結(jié)合起來(lái)得到一個(gè)值返回Optional |
reduce(T iden,BinaryOperator p) | 將流中的元素反復(fù)結(jié)合起來(lái)得到一個(gè)值T |
演示:
Optional<Double> reduce = employees.stream().map(Employee::getTotalSalary).reduce(Double::sum); double v = reduce.get(); System.out.println("reduce="+v); int[] array = {1, 4, 6, 7, 12}; System.out.println("===="); // 這里第一次將0作為x的值然后數(shù)組中1作為y,然后計(jì)算后的結(jié)果是1,第二次將1作為x的值,然后數(shù)組中的4作為y值進(jìn)行相加,后面以此類(lèi)推,直到將所有的值都進(jìn)行相加 int reduce = Arrays.stream(array).reduce(0, (x, y) -> x + y); System.out.println("reduce=" + reduce);
- 收集
方法 | 方法介紹 |
---|---|
collect(Collector c) | 將流轉(zhuǎn)換為其他形式,接收Collector接口實(shí)現(xiàn)。 |
下面將對(duì)Stream做一個(gè)總體的回顧和使用。
@Test public void test() { // 避免空指針異常 Optional<Employee> optional = employees.stream().sorted((e1, e2) -> e1.getTotalSalary().compareTo(e2.getTotalSalary())).findFirst(); // 若空指針異常就怎么處理 optional.orElse(new Employee()); System.out.println(optional); // 返回任意一個(gè)(并行開(kāi)啟多個(gè)線程查找) Optional<Employee> any = employees.parallelStream().filter((e) -> e.getAge() > 25).findAny(); System.out.println(any); // Max(Comparator按年齡比較) Optional<Employee> max = employees.stream().max(Comparator.comparing((e) -> e.getAge())); System.out.println(max); Optional<Double> max1 = employees.stream().map(Employee::getTotalSalary().max(Double::compare); System.out.println(max1.get()); // 流中元素接收,計(jì)算得到一個(gè) List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); Integer reduce = list.stream().reduce(0, (x, u) -> x + u);//0+1+2.....+10 System.out.println(reduce); Optional<Integer> reduce1 = employees.stream().map(Employee::getAge).reduce(Integer::sum); System.out.println(reduce1.get() + "----------"); // 收集元素到list employees.stream().map(Employee::getName).distinct().collect(Collectors.toList()).forEach(System.out::println); System.out.println(); // 收集元素到LinkList employees.stream().map(Employee::getName).distinct().collect(Collectors.toCollection(HashSet::new)).forEach(System.out::println); // 獲取流中最大值 Optional<Integer> max2 = employees.stream().map(Employee::getAge).max(Integer::compare); System.out.println(max2.get()); // 收集獲取總數(shù)(集合總數(shù)) Long collect = employees.stream().collect(Collectors.counting()); System.out.println(collect); // 工資的平均值 Double collect1 = employees.stream().collect(Collectors.averagingDouble(Employee::getTotalSalary)); System.out.println(collect1); // 獲取工資的總數(shù),綜合,最小值,平均值,最大值 DoubleSummaryStatistics collect2 = employees.stream().collect(Collectors.summarizingDouble(Employee::getTotalSalary)); System.out.println(collect2); // 獲取年齡最大的員工 Optional<Employee> collect3 = employees.stream().collect(Collectors.maxBy((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()))); System.out.println(collect3.get()); // 獲取年齡最小的員工 Optional<Double> collect4 = employees.stream().map(Employee::getTotalSalary).collect(Collectors.minBy(Double::compare)); System.out.println(collect4.get()); // 按薪資分組 Map<Double, List<Employee>> collect5 = employees.stream().collect(Collectors.groupingBy(Employee::getTotalSalary)); System.out.println(collect5); // 薪資分區(qū)(匹配true) Map<Boolean, List<Employee>> collect6 = employees.stream().collect(Collectors.partitioningBy((e) -> e.getTotalSalary() > 5000d)); System.out.println(collect6); }
總結(jié)
到這里Stream介紹基本完成了。以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java兩種方法計(jì)算出階乘尾部連續(xù)0的個(gè)數(shù)
這篇文章主要介紹了Java兩種方法計(jì)算出階乘尾部連續(xù)0的個(gè)數(shù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03學(xué)會(huì)在Java中使用Optional功能
這篇文章主要介紹了學(xué)會(huì)在Java中使用Optional功能,在本文中,我們將了解如何、何時(shí)以及在哪里最好地應(yīng)用Optional,具體相關(guān)內(nèi)容需要的朋友可以參考下面文章內(nèi)容2022-09-09在java中 利用匿名內(nèi)部類(lèi)進(jìn)行較簡(jiǎn)潔的雙括弧初始化的方法
本篇文章小編將為大家介紹,關(guān)于在java中 利用匿名內(nèi)部類(lèi)進(jìn)行較簡(jiǎn)潔的雙括弧初始化的方法,有需要的朋友可以參考一下2013-04-04使用Spring Security集成手機(jī)驗(yàn)證碼登錄功能實(shí)現(xiàn)
本文詳細(xì)介紹了如何利用SpringSecurity來(lái)實(shí)現(xiàn)手機(jī)驗(yàn)證碼的注冊(cè)和登錄功能,在登錄過(guò)程中,同樣需通過(guò)驗(yàn)證碼進(jìn)行驗(yàn)證,文章還提供了相關(guān)的代碼實(shí)現(xiàn)2024-10-10MyBatis使用嵌套查詢(xún)collection和association的實(shí)現(xiàn)
本文詳細(xì)介紹了使用MyBatis框架進(jìn)行數(shù)據(jù)庫(kù)操作時(shí),如何利用collection標(biāo)簽實(shí)現(xiàn)一對(duì)多的嵌套查詢(xún)和使用association標(biāo)簽實(shí)現(xiàn)一對(duì)一的嵌套查詢(xún),感興趣的可以了解一下2024-09-09詳解Spring Cloud Gateway修改請(qǐng)求和響應(yīng)body的內(nèi)容
這篇文章主要介紹了Spring Cloud Gateway修改請(qǐng)求和響應(yīng)body的內(nèi)容的相關(guān)資料,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09java實(shí)現(xiàn)切割wav音頻文件的方法詳解【附外部jar包下載】
這篇文章主要介紹了java實(shí)現(xiàn)切割wav音頻文件的方法,結(jié)合實(shí)例形式詳細(xì)分析了java切割wav音頻文件的相關(guān)原理、操作技巧與注意事項(xiàng),并附帶外部jar包供讀者下載,需要的朋友可以參考下2019-05-05