您好,登錄后才能下訂單哦!
這篇文章主要講解了“怎么從零開始學習Java8 Stream”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“怎么從零開始學習Java8 Stream”吧!
在我們平常的開發中幾乎每天都會有到List、Map等集合API,若是問Java什么API使用最多,我想也應該是集合了。舉例:假如我有個集合List,里面元素有1,7,3,8,2,4,9
,需要找出里面大于5的元素,具體實現代碼:
public List<integer> getGt5Data() { List<integer> data = Arrays.asList(1, 7, 3, 8, 2, 4, 9); List<integer> result = new ArrayList<>(); for (Integer num : data) { if (num > 5) { result.add(num); } } return result; }
這個實現讓我們感覺到了集合的操作不是太完美,如果是數據庫的話,我們只需要簡單的在where后面加一個條件大于5就可以得到我們想要的結果,為什么Java的集合就沒有這種API呢? 其次,如果我們遇到有大集合需要處理,為了提高性能,我們可能需要使用到多線程來處理,但是寫并行程序的復雜度有提高了不少。
基于以上的問題,所有Java8推出了Stream
Stream有哪些特點:
元素的序列:與集合一樣可以訪問里面的元素,集合講的是數據,而流講的是操作,比如:filter、map
源: 流也需要又一個提供數據的源,順序和生成時的順序一致
數據的操作:流支持類似于數據庫的操作,支持順序或者并行處理數據;上面的例子用流來實現會更加的簡潔
public List<integer> getGt5Data() { return Stream.of(1, 7, 3, 8, 2, 4, 9) .filter(num -> num > 5) .collect(toList()); }
流水線操作:很多流的方法本身也會返回一個流,這樣可以把多個操作連接起來,形成流水線操作
內部迭代:與以往的迭代不同,流使用的內部迭代,用戶只需要專注于數據處理
只能遍歷一次: 遍歷完成之后我們的流就已經消費完了,再次遍歷的話會拋出異常
Java8中的Stream定義了很多方法,基本可以把他們分為兩類:中間操作、終端操作;要使用一個流一般都需要三個操作:
定義一個數據源
定義中間操作形成流水線
定義終端操作,執行流水線,生成計算結果
使用Stream.of
方法構建一個流
Stream.of("silently","9527","silently9527.cn") .forEach(System.out::println);
使用數組構建一個流
int[] nums = {3, 5, 2, 7, 8, 9}; Arrays.stream(nums).sorted().forEach(System.out::println);
通過文件構建一個流 使用java.nio.file.Files.lines方法可以輕松構建一個流對象
Files.lines(Paths.get("/Users/huaan9527/Desktop/data.txt")) .forEach(System.out::println);
中間操作會返回另外一個流,這樣可以讓多個操作連接起來形成一個流水線的操作,只要不觸發終端操作,那么這個中間操作都不會實際執行。
該操作接受一個返回boolean的函數,當返回false的元素將會被排除掉
舉例:假如我們100個客戶,需要篩選出年齡大于20歲的客戶
List<customer> matchCustomers = allCustomers.stream() .filter(customer -> customer.getAge()>20) .collect(toList());
該操作將會排除掉重復的元素
List<integer> data = Stream.of(1, 7, 3, 8, 2, 4, 9, 7, 9) .filter(num -> num > 5) .distinct() .collect(toList());
該方法限制流只返回指定個數的元素
List<integer> data = Stream.of(1, 7, 3, 8, 2, 4, 9, 7, 9) .filter(num -> num > 5) .limit(2) .collect(toList());
扔掉前指定個數的元素;配合limit使用可以達到翻頁的效果
List<integer> data = Stream.of(1, 7, 3, 8, 2, 4, 9, 7, 9) .filter(num -> num > 5) .skip(1) .limit(2) .collect(toList());
該方法提供一個函數,流中的每個元素都會應用到這個函數上,返回的結果將形成新類型的流繼續后續操作。 舉例:假如我們100個客戶,需要篩選出年齡大于20歲的客戶,打印出他們的名字
allCustomers.stream() .filter(customer -> customer.getAge() > 20) .map(Customer::getName) .forEach(System.out::println);
在調用map之前流的類型是Stream<customer>
,執行完map之后的類型是Stream<string>
假如我們需要把客戶的名字中的每個字符打印出來,代碼如下:
List<customer> allCustomers = Arrays.asList(new Customer("silently9527", 30)); allCustomers.stream() .filter(customer -> customer.getAge() > 20) .map(customer -> customer.getName().split("")) .forEach(System.out::println);
執行本次結果,你會發現沒有達到期望的結果,打印的結果
[Ljava.lang.String;@38cccef
這是因為調用map之后返回的流類型是Stream<string[]>
,所有forEach的輸入就是String[]
;這時候我們需要使用flatMap把String[]
中的每個元素都轉換成一個流,然后在把所有的流連接成一個流,修改后的代碼如下
List<customer> allCustomers = Arrays.asList(new Customer("silently9527", 30)); allCustomers.stream() .filter(customer -> customer.getAge() > 20) .map(customer -> customer.getName().split("")) .flatMap(Arrays::stream) .forEach(System.out::println);
執行結果:
對所有的元素進行排序
List<integer> numbers = Arrays.asList(1, 7, 3, 8, 2, 4, 9); numbers.stream().sorted(Integer::compareTo).forEach(System.out::println);
終端操作會執行所有的中間操作生成執行的結果,執行的結果不在是一個流。
如果流中有一個元素滿足條件將返回true
if (allCustomers.stream().anyMatch(customer -> "silently9527".equals(customer.getName()))) { System.out.println("存在用戶silently9527"); }
確保流中所有的元素都能滿足
if (allCustomers.stream().allMatch(customer -> customer.getAge() > 20)) { System.out.println("所有用戶年齡都大于20"); }
與allMatch操作相反,確保流中所有的元素都不滿足
if (allCustomers.stream().noneMatch(customer -> customer.getAge() < 20)) { System.out.println("所有用戶年齡都大于20"); }
返回流中的任意一個元素,比如返回大于20歲的任意一個客戶
Optional<customer> optional = allCustomers.stream() .filter(customer -> customer.getAge() > 20) .findAny();
返回流中的第一個元素
Optional<customer> optional = allCustomers.stream() .filter(customer -> customer.getAge() > 20) .findFirst();
接受兩個參數:一個初始值,一個BinaryOperator<t> accumulator
將兩個元素合并成一個新的值 比如我們對一個數字list累加
List<integer> numbers = Arrays.asList(1, 7, 3, 8, 2, 4, 9); Integer sum = numbers.stream().reduce(0, (a, b) -> a + b);
上面的代碼,我們可以簡寫
Integer reduce = numbers.stream().reduce(0, Integer::sum);
找出流中的最大值、最小值 min、max
numbers.stream().reduce(Integer::max) numbers.stream().reduce(Integer::min)
統計流中元素的個數
numbers.stream().count()
在Java8中已經預定義了很多收集器,我們可以直接使用,所有的收集器都定義在了Collectors
中,基本上可以把這些方法分為三類:
將元素歸約和匯總成一個值
分組
分區
先看下我們之前求最大值和最小值采用收集器如何實現
找出年齡最大和最小的客戶
Optional<customer> minAgeCustomer = allCustomers.stream().collect(minBy(Comparator.comparing(Customer::getAge))); Optional<customer> maxAgeCustomer = allCustomers.stream().collect(maxBy(Comparator.comparing(Customer::getAge)));
求取年齡的平均值
Double avgAge = allCustomers.stream().collect(averagingInt(Customer::getAge));
進行字符串的連接
把客戶所有人的名字連接成一個字符串用逗號分隔
allCustomers.stream().map(Customer::getName).collect(joining(","));
在數據庫的操作中,我們可以輕松的實現通過一個屬性或者多個屬性進行數據分組,接下來我們看看Java8如何來實現這個功能。
根據客戶的年齡進行分組
Map<integer, list<customer>> groupByAge = allCustomers.stream().collect(groupingBy(Customer::getAge));
Map的key就是分組的值年齡,List<customer>
就是相同年齡的用戶
我們需要先按照用戶的地區分組,在按年齡分組
Map<string, map<integer, list<customer>>> groups = allCustomers.stream() .collect(groupingBy(Customer::getArea, groupingBy(Customer::getAge)));
在相對于普通的分組,這里多傳了第二個參數又是一個groupingBy
;理論上我們可以通過這個方式擴展到n層分組
分組后再統計數量
Map<string, long> groupByCounting = allCustomers.stream() .collect(groupingBy(Customer::getArea, counting()));
以用戶所在地區分組后找出年齡最大的用戶
Map<string, optional<customer>> optionalMap = allCustomers.stream() .collect(groupingBy(Customer::getArea, maxBy(Comparator.comparing(Customer::getAge))));
這時候返回的Map中的value被Optional包裹,如果我們需要去掉Optional,可以使用collectingAndThen
Map<string, customer> customerMap = allCustomers.stream() .collect(groupingBy(Customer::getArea, collectingAndThen(maxBy(Comparator.comparing(Customer::getAge)), Optional::get) ));
感謝各位的閱讀,以上就是“怎么從零開始學習Java8 Stream”的內容了,經過本文的學習后,相信大家對怎么從零開始學習Java8 Stream這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。