《Java8實戰》-讀書筆記第一章(02)

《Java8實戰》-讀書筆記第一章(02)

從方法傳遞到Lambda

接着上次的Predicate,繼續來了解一下,若是繼續簡化代碼。java

把方法做爲值來傳遞雖然頗有用,可是要是有不少相似與isHeavyApple和isGreenApple這種可能只用一兩次的方法定義一堆確實有點煩人。爲了解決這個問題,Java8它引入了一套新記法(匿名函數或Lambda),然你能夠這樣寫:git

List<Apple> isRedApples = filterApples(FilteringApples.apples, apple -> "red".equals(apple.getColor()));

或者是:github

List<Apple> appleList = filterApples(FilteringApples.apples, apple -> apple.getWeight() < 120
                && "red".equals(apple.getColor()));

甚至,你均可以不須要使用filterApples這個方法了,直接使用Stream中的filter方法就能夠解決了:app

List<Apple> isGreenApple = apples.stream().filter(apple -> "green".equals(apple.getColor()))
                .collect(Collectors.toList());

酷,看起來很不錯。因此,你甚至都不須要爲只用一次的方法寫定義;這樣的代碼看起來更簡潔、更清晰,由於你用不着去找本身到底傳遞了什麼代碼。函數

在剛剛篩選蘋果的過程當中,就有使用到Stream(流)其中的一個方法,這個Stream和InputStream、OutputStream是兩個徹底不一樣的東西。Stream它是Java8中的一個核心新特,它是一套新的用來處理集合的API,有不少相似與filter這樣的方法並且使用起來很是的簡單和簡潔,能夠簡化大部分代碼而且在並行的狀況下利用多核CPU,能頗有效的提高對集合處理的性能。性能

本章只是簡單的介紹了一下流的使用方式,至於流的詳細用法後面的章節會提到的。優化

如今,有一串字符串,須要進行篩選而且轉爲大寫以進行排序,在Java8以前是咱們是這麼幹的:this

List<String> stringList = Arrays.asList("a1", "a2", "b1", "c1", "c2", "c4", "c3");

List<String> cList = new ArrayList<>();
for (String s : stringList) {
    // 篩選出以c開頭的字符串
    if (s.startsWith("c")) {
        // 將以c開頭的字符串轉爲大寫,添加到集合
        cList.add(s.toUpperCase());
    }
}

// 排序
Collections.sort(cList);

// 遍歷打印
for (String s : cList) {
    System.out.println(s);
}

這樣的代碼看起來很頭疼,須要寫這麼長一段的代碼,在Java8中能夠使用Stream進行優化:code

List<String> stringList = Arrays.asList("a1", "a2", "b1", "c1", "c2", "c4", "c3");

stringList.stream()
        // 篩選出以c開頭的字符串
        .filter(s -> s.startsWith("c"))
        // 將剛剛以c開頭的字符串轉爲大寫
        .map(String::toUpperCase)
        // 排序
        .sorted()
        // 循環遍歷
        .forEach(System.out::println);

太棒了,只須要短短的一行代碼就能夠完成!可是,使用Stream它也是有缺點的,它的性能不如foreach的效率高爲了解決這個問題,Stream支持並。使用並行能極大的利用多核CPU的優點,例如說:這些代碼本來只是用單核進行處理,如今有一臺8核的CPU電腦,那麼它的處理速度就會是單核的八倍。排序

咱們來進行比較一下,生成一個0-100的數字並寫入到文件中,循序流VS並行流誰的效率更高.

循序流:

long startTime = System.currentTimeMillis();
OutputStream out = new FileOutputStream(new File("D:/integer1.txt"));

IntStream.rangeClosed(0, 100)
        .forEach(i -> {
            try {
                Thread.sleep(100L);
                out.write(i);
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        });

long endTime = System.currentTimeMillis();
System.out.println("循序流:" + (endTime - startTime));

並行流:

long startTime = System.currentTimeMillis();
OutputStream out = new FileOutputStream(new File("D:/integer2.txt"));

IntStream.rangeClosed(0, 100)
        .parallel().forEach(i -> {
    try {
        Thread.sleep(100L);
        out.write(i);
    } catch (IOException | InterruptedException e) {
        e.printStackTrace();
    }
});

long endTime = System.currentTimeMillis();
System.out.println("並行流:" + (endTime - startTime));

執行結果(I5-6200U的筆記本上執行結果):

循序流:10251
並行流:2620

效率明顯要比循序流快不少嘛!可是,並行流並非萬能的,若是把sleep去掉後而且數字加到100萬,你會發現運行的時間比循序流還要長。

去掉sleep而且生成的數字是0-100萬,所消耗的時間:

循序流:2775
並行流:3346

至於爲何有時候並行流效率比循序流還低,這個之後的文章會解釋。

默認方法

默認方法是Java8中的一個新特性,它的出現使得接口的升級變得平滑了,由於子類不是必須再去顯式的實現接口中的方法了。

例如:在Java8中,你能夠直接調用List接口中的sort方法、它是用Java8 List接口中以下所示的默認方法實現的:

default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {
        i.next();
        i.set((E) e);
    }
}

這意味着List的任何實體類都不須要顯式的實現sort,而在之前的Java版本中,除非提供了sort的實現,不然這些實體類都沒法編譯經過。可是,默認方法也存在着一些問題,一個類能夠實現多個接口,那麼好幾個接口多有一樣的默認方法,那麼這是否意味着Java中有了某種形式的多繼承?若是是多繼承,那麼會不會出現像C++中菱形繼承的問題?這些問題之後的文章中都會有解釋和解決方案。

第一章總結:

  1. 瞭解了Java8中的一些核心新特性,例如:Lambda表達式、Stream、默認方法。
  2. 瞭解了Lambda表達式和Stream爲代碼帶來的簡潔性。
  3. 並行流帶來的好處。
  4. Java8中的默認方法帶來的好處。

代碼案例:

chap1

相關文章
相關標籤/搜索