1. 程式人生 > >java8新特性學習:stream與lambda

java8新特性學習:stream與lambda

包含 term strong java statistic 管道 特定 getname java8新特性

Streams api

技術分享圖片

對 Stream 的使用就是實現一個 filter-map-reduce 過程,產生一個最終結果,或者導致一個副作用(side effect)。

流的操作類型分為兩種:

  • Intermediate:一個流可以後面跟隨零個或多個 intermediate 操作。其目的主要是打開流,做出某種程度的數據映射/過濾,然後返回一個新的流,交給下一個操作使用。這類操作都是惰性化的(lazy),就是說,僅僅調用到這類方法,並沒有真正開始流的遍歷。
  • Terminal:==一個流只能有一個 terminal 操作==,當這個操作執行後,流就被使用“光”了,無法再被操作。所以這必定是流的最後一個操作。Terminal 操作的執行,才會真正開始流的遍歷,並且會生成一個結果,或者一個 side effect。
  • short-circuiting。用以指:
    對於一個 intermediate 操作,如果它接受的是一個無限大(infinite/unbounded)的 Stream,但返回一個有限的新 Stream。
    對於一個 terminal 操作,如果它接受的是一個無限大的 Stream,但能在有限的時間計算出結果。
    當操作一個無限大的 Stream,而又希望在有限時間內完成操作,則在管道內擁有一個 short-circuiting 操作是必要非充分條件。

map/flatMap

map 生成的是個 1:1 映射,每個輸入元素,都按照規則轉換成為另外一個元素。還有一些場景,是一對多映射關系的,這時需要 flatMap。

reduce

主要作用是把 Stream 元素組合起來。它提供一個起始值(種子),然後依照運算規則(BinaryOperator),和前面 Stream 的第一個、第二個、第 n 個元素組合。從這個意義上說,字符串拼接、數值的 sum、min、max、average 都是特殊的 reduce。

Optional

Optional 中我們真正可依賴的應該是==除了 isPresent() 和 get() 的其他方法:==

public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
public T orElse(T other)
public T orElseGet(Supplier<? extends T> other)
public void ifPresent(Consumer<? super T> consumer)
public Optional<T> filter(Predicate<? super T> predicate)
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

構造方法

Optional 的三種構造方式:

  1. Optional.of(obj),
  2. Optional.ofNullable(obj) 和
  3. 明確的 Optional.empty()
  • 存在即返回, 無則提供默認值
return user.orElse(null);  //而不是 return user.isPresent() ? user.get() : null;
return user.orElse(UNKNOWN_USER);
  • 存在即返回, 無則由函數來產生
return user.orElseGet(() -> fetchAUserFromDatabase()); //而不要 return user.isPresent() ? user: fetchAUserFromDatabase();
  • 存在才對它做點什麽

    user.isPresent(System.out::println);
  • map 函數隆重登場

當 user.isPresent() 為真, 獲得它關聯的 orders, 為假則返回一個空集合時, 我們用上面的 orElse, orElseGet 方法都乏力時, 那原本就是 map 函數的責任

return user.map(u -> u.getOrders()).orElse(Collections.emptyList())

return user.map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);
  • flatMap

flatMap方法與map方法類似,區別在於mapping函數的返回值不同。map方法的mapping函數返回值可以是任何類型T,而flatMap方法的mapping函數必須是Optional。

upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));//輸出SANAULLA
  • filter

如果有值並且滿足斷言條件返回包含該值的Optional,否則返回空Optional。

Optional<String> longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters"));//輸出Sanaulla
 
//另一個例子是Optional值不滿足filter指定的條件。
Optional<String> anotherName = Optional.of("Sana");
Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
//輸出:name長度不足6字符
System.out.println(shortName.orElse("The name is less than 6 characters"));

lambda

Java中Lambda表達式的使用

雖然看著很先進,其實Lambda表達式的本質只是一個"語法糖",由編譯器推斷並幫你轉換包裝為常規的代碼,因此你可以使用更少的代碼來實現同樣的功能。

建議不要亂用,因為這就和某些很高級的黑客寫的代碼一樣,簡潔,難懂,難以調試,維護人員想罵娘.

當開發者在編寫Lambda表達式時,也會隨之被編譯成一個函數式接口。

lambda表達式有個限制,那就是只能引用 final 或 final 局部變量,這就是說不能在lambda內部修改定義在域外的變量。

Lambda表達式的語法
基本語法:
(params) -> expression
(params) -> statement
(params) -> { statements }
// 1. 不需要參數,返回值為 5  
() -> 5  
  
// 2. 接收一個參數(數字類型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2個參數(數字),並返回他們的差值  
(x, y) -> x – y  
  
// 4. 接收2個int型整數,返回他們的和  
(int x, int y) -> x + y  
  
// 5. 接受一個 string 對象,並在控制臺打印,不返回任何值(看起來像是返回void)  
(String s) -> System.out.print(s)
players.forEach((player) -> System.out.print(player + "; "));  
   
// 在 Java 8 中使用雙冒號操作符(double colon operator)  
players.forEach(System.out::println);  
// 使用匿名內部類  
btn.setOnAction(event -> System.out.println("Hello World!"));  

show.addActionListener((e) -> {
    System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
});
new Thread(() -> System.out.println("Hello world !")).start(); 
Runnable race2 = () -> System.out.println("Hello world !"); 

//排序
Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));   
  • 如何在lambda表達式中加入Predicate

    Predicate<String> startsWithJ = (n) -> n.startsWith("J");
    Predicate<String> fourLetterLong = (n) -> n.length() == 4;
    names.stream()
    .filter(startsWithJ.and(fourLetterLong))
    .forEach((n) -> System.out.print("nName, which starts with ‘J‘ and four letter long is : " + n));
  • Java 8中使用lambda表達式的Map和Reduce示例

    List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
    double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
  • 通過過濾創建一個String列表

    // 創建一個字符串列表,每個字符串長度大於2
    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);
  • 對列表的每個元素應用函數

    // 將字符串換成大寫並用逗號鏈接起來
    List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
    String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
    System.out.println(G7Countries);
  • 計算集合元素的最大值、最小值、總和以及平均值

    //獲取數字的個數、最小值、最大值、總和以及平均值
    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());

方法引用

  • 第一種方法引用是構造器引用,它的語法是Class::new,或者更一般的Class< T >::new。
  • 第二種方法引用是靜態方法引用,它的語法是Class::static_method。
  • 第三種方法引用是特定類的任意對象的方法引用,它的語法是Class::method。請註意,這個方法沒有參數。
String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
The equivalent lambda expression for the method reference String::compareToIgnoreCase would have the formal parameter list (String a, String b), where a and b are arbitrary names used to better describe this example. The method reference would invoke the method a.compareToIgnoreCase(b).


public void repair() {   
        System.out.println( "Repaired " + this.toString() );
    }
cars.forEach( Car::repair );
  • 第四種方法引用是特定對象的方法引用,它的語法是instance::method。請註意,這個方法接受一個Car類型的參數
class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }
        
    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);


final Car police = Car.create( Car::new );
cars.forEach( police::follow );
public void follow( final Car another ) {
        System.out.println( "Following the " + another.toString() );
    }

Java虛擬機(JVM)的新特性

PermGen空間被移除了,取而代之的是Metaspace(JEP 122)。JVM選項-XX:PermSize與-XX:MaxPermSize分別被-XX:MetaSpaceSize與-XX:MaxMetaspaceSize所代替。

參考

  • 使用 Java8 Optional 的正確姿勢
  • Java 8 Optional類深度解析
  • Java8 lambda表達式10個示例
  • Java 8 中的 Streams API 詳解
  • Java 8新特性終極指南

tips:本文屬於自己學習和實踐過程的記錄,很多圖和文字都粘貼自網上文章,沒有註明引用請包涵!如有任何問題請留言或郵件通知,我會及時回復。

java8新特性學習:stream與lambda