Java8 Stream中的常用方法展示
阿新 • • 發佈:2018-12-25
其實Streanm 和常規的一些操作效能差別並不太大,關鍵在於你如何使用。
- 對於簡單操作,比如最簡單的遍歷,Stream序列API效能明顯差於顯示迭代,但並行的Stream API能夠發揮多核特性。
- 對於複雜操作,Stream序列API效能可以和手動實現的效果匹敵,在並行執行時Stream API效果遠超手動實現。
所以,如果出於效能考慮,1. 對於簡單操作推薦使用外部迭代手動實現,2. 對於複雜操作,推薦使用Stream API, 3. 在多核情況下,推薦使用並行Stream API來發揮多核優勢,4.單核情況下不建議使用並行Stream API。
如果出於程式碼簡潔性考慮,使用Stream API能夠寫出更短的程式碼。即使是從效能方面說,儘可能的使用Stream API也另外一個優勢,那就是隻要Java Stream類庫做了升級優化,程式碼不用做任何修改就能享受到升級帶來的好處。
package com.whb; import java.util.Arrays; import java.util.Comparator; import java.util.IntSummaryStatistics; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.Test; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import lombok.Data; /** * @author whb * @date 2018年10月29日 下午4:28:07 * @Description: JDK8的stram方法演示 * 如果一個方法接受聲明於 java.util.function 包內的介面,例如 Predicate、Function、Consumer 或 Supplier,那麼可以向其傳lambda表示式. * lambda表示式內可以使用方法引用,僅當該方法不修改lambda表示式提供的引數。本例中的lambda表示式可以換為方法引用,因為這僅是一個引數相同的簡單方法呼叫。 */ public class LambdaTest { /** * lambda表示式內可以使用方法引用,僅當該方法不修改lambda表示式提供的引數。本例中的lambda表示式可以換為方法引用,因為這僅是一個引數相同的簡單方法呼叫。 * list.forEach(n -> System.out.println(n)); * list.forEach(System.out::println); // 使用方法引用 * 然而,若對引數有任何修改,則不能使用方法引用,而需鍵入完整地lambda表示式,如下所示: * list.forEach((String s) -> System.out.println("*" + s + "*")); * 事實上,可以省略這裡的lambda引數的型別宣告,編譯器可以從列表的類屬性推測出來。 */ @Test public void testForEach() { List<Integer> list = Lists.newArrayList(); list.add(1); list.add(2); list.add(3); // 直接列印 list.forEach(System.out::println); list.forEach(Integer::floatValue); // 取值分別操作 list.stream().forEach(i -> { System.out.println(i * 3); }); // 可改變物件,只在本次呼叫中有效, 並不會改變原有的list list.stream().map((i) -> i * 3).forEach(System.out::println); // 不可改變元有物件 list.forEach(i -> i = i * 3); list.forEach(System.out::println); Integer integer = list.stream().map((i) -> i = i * 3).reduce((sum, count) -> sum += count).get(); System.out.println(integer); } @Test public void test2() { new Thread(() -> System.out.println("In Java8!")).start(); System.out.println(Joiner.on("_").join("11", "22", "33")); List<String> languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp"); System.out.println("Languages which starts with J :"); filter(languages, (str) -> ((String) str).startsWith("J")); System.out.println("Languages which ends with a "); filter(languages, (str) -> ((String) str).endsWith("a")); System.out.println("Print all languages :"); filter(languages, (str) -> true); System.out.println("Print no language : "); filter(languages, (str) -> false); System.out.println("Print language whose length greater than 4:"); filter(languages, (str) -> ((String) str).length() > 4); Predicate<String> startWithJ = (n) -> n.startsWith("J"); Predicate<String> fourLength = (n) -> n.length() == 4; languages.stream().filter(startWithJ.and(fourLength)).forEach(System.out::println); } /** * redicate介面 * @param names * @param condition */ private void filter(List<String> names, Predicate<String> condition) { for (String name : names) { if (condition.test(name)) { System.out.println(name + " "); } } } /** * reduce, 用來將值進行合併, 又稱摺疊操作, Map和Reduce操作是函數語言程式設計的核心操作 * SQL中類似 sum()、avg() 或者 count() 的聚集函式,實際上就是 reduce 操作,因為它們接收多個值並返回一個值。 * 流API定義的 reduceh() 函式可以接受lambda表示式,並對所有值進行合併。 * IntStream這樣的類有類似 average()、count()、sum() 的內建方法來做 reduce 操作, * 也有mapToLong()、mapToDouble() 方法來做轉換 */ @Test public void test3() { // 字串連線,concat = "ABCD" String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); System.out.println(concat); // 求最小值,minValue = -3.0 double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); System.out.println(minValue); // 求和,sumValue = 10, 有起始值 int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum); System.out.println(sumValue); // 求和,sumValue = 10, 無起始值 sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get(); // 過濾,字串連線,concat = "ace" concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat); System.out.println("a".concat("b")); } /** * 過濾是Java開發者在大規模集合上的一個常用操作,而現在使用lambda表示式和流API過濾大規模資料集合是驚人的簡單。 * 流提供了一個 filter() 方法,接受一個 Predicate 物件,即可以傳入一個lambda表示式作為過濾邏輯。 * 下面的例子是用lambda表示式過濾Java集合,將幫助理解。 */ @Test public void test4() { List<String> strList = Arrays.asList("abc", "eqwr", "bcd", "qb", "ehdc", "jk"); List<String> filtered = strList.stream().filter(x -> x.length() > 2).collect(Collectors.toList()); System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered); // 對列表的每個元素使用 函式 strList = Lists.newArrayList("abc", "eqwr", "bcd", "qb", "ehdc", "jk"); String collect = strList.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", ")); System.out.printf("filtered list : %s %n", collect); // 使用distinct進行去重 List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4); List<Integer> distinct = numbers.stream().map(i -> i * i).distinct().collect(Collectors.toList()); System.out.printf("Original List : %s, Square Without duplicates : %s %n", numbers, distinct); } /** * 計算最值和平均值 * IntStream、LongStream 和 DoubleStream 等流的類中,有個非常有用的方法叫做 summaryStatistics() 。 * 可以返回 IntSummaryStatistics、LongSummaryStatistics 或者 DoubleSummaryStatistic s,描述流中元素的各種摘要資料。 * 在本例中,我們用這個方法來計算列表的最大值和最小值。它也有 getSum() 和 getAverage() 方法來獲得列表的所有元素的總和及平均值 */ @Test public void test5() { // 獲取數字的個數、最小值、最大值、總和以及平均值 List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29); IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("Highest prime number in List : " + stats.getMax()); System.out.println("Lowest prime number in List : " + stats.getMin()); System.out.println("Sum of all prime numbers : " + stats.getSum()); System.out.println("Average of all prime numbers : " + stats.getAverage()); } /** * 使用方法引用, 不對引數做任何修改方法引用有3種形式 * 把lambda表示式的引數直接當成instanceMethod|staticMethod的引數來呼叫。 * 比如System.out::println等同於x->System.out.println(x);Math::max等同於(x, y)->Math.max(x,y)。 * objectName::instanceMethod * ClassName::staticMethod * 把lambda表示式的第一個引數當成instanceMethod的目標物件,其他剩餘引數當成該方法的引數。 * 比如String::toLowerCase等同於x->x.toLowerCase()。 * ClassName::instanceMethod */ @Test public void test6() { // 使用String預設的排序規則,比較的是Person的name欄位 Comparator<Student> byName = Comparator.comparing(p -> p.getName()); // 不用寫傳入引數,傳入的用Person來宣告 Comparator<Student> byName2 = Comparator.comparing(Student::getName); List<Student> studentList = Arrays.asList(new Student("網三", 11), new Student("彰武", 22), new Student("趙四", 33)); // 獲取執行緒安全的Map ConcurrentHashMap<String, Student> currrentMap = (ConcurrentHashMap<String, Student>) studentList.stream() .collect(Collectors.toConcurrentMap(Student::getName, Student -> Student)); // 老的方式 Map<String, Integer> map = studentList.stream().collect(Collectors.toMap(new Function<Student, String>() { @Override public String apply(Student t) { return t.getName(); } }, Student::getAge)); } /** * 實體類 */ @Data class Student { private String name; private Integer age; public Student(String name, Integer age) { super(); this.name = name; this.age = age; } } }