java8 lambda表達式與集合類批處理操作

jopen 10年前發布 | 34K 次閱讀 Java8 Java開發

λ(lambda)表達式本質上是一個匿名方法,本文主要講解 lambda表達式與集合類批處理操作

一、基本概念

    λ表達式可以被當做是一個Object。λ表達式的類型,叫做“目標類型(target type)”。λ表達式的目標類型是“函數接口(functional interface)”,這是Java8新引入的概念。它的定義是:一個接口,如果只有一個顯式聲明的抽象方法,那么它就是一個函數接口。一般用@FunctionalInterface標注出來(也可以不標)

@FunctionalInterface
public interface Runnable { void run(); }

public interface Callable<V> { V call() throws Exception; }

public interface ActionListener { void actionPerformed(ActionEvent e); }

public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }
    注意最后這個Comparator接口。它里面聲明了兩個方法,貌似不符合函數接口的定義,但它的確是函數接口。這是因為equals方法是Object的,所有的接口都會聲明Object的public方法——雖然大多是隱式的。所以,Comparator顯式的聲明了equals不影響它依然是個函數接口

    集合類的批處理操作API的目的是實現集合類的“內部迭代”,并期望充分利用現代多核CPU進行并行計算。
    Java8之前集合類的迭代(Iteration)都是外部的,即客戶代碼,不能充分利用cpu的多核資源。而內部迭代意味著改由Java類庫來進行迭代,而不是客戶代碼.

    Java8為集合類引入了一個重要概念:流(stream)。一個流通常以一個集合類實例為其數據源,然后在其上定義各種操作。流的API設計使用了管道(pipelines)模式。對流的一次操作會返回另一個流。如同IO的API或者StringBuffer的append方法那樣,從而多個不同的操作可以在一個語句里串起來.

二、λ表達式的使用

    一個λ表達式只有在轉型成一個函數接口后才能被當做Object使用

    可以用一個λ表達式為一個函數接口賦值,然后再賦值給一個Object

    一個λ表達式可以有多個目標類型(函數接口),只要函數匹配成功即可。但需注意一個λ表達式必須至少有一個目標類型

    λ表達式主要用于替換以前廣泛使用的內部匿名類,各種回調,比如事件響應器、傳入Thread類的Runnable等

new Thread( () -> {
        System.out.println("This is from an anonymous method (lambda exp).");
    } );
    注意線程里的λ表達式,你并不需要顯式地把它轉成一個Runnable,因為Java能根據上下文自動推斷出來:一個Thread的構造函數接受一個Runnable參數,而傳入的λ表達式正好符合其run()函數,所以Java編譯器推斷它為Runnable。

    filter方法的參數是Predicate類型,forEach方法的參數是Consumer類型,它們都是函數接口,所以可以使用λ表達式

三、代碼樣例

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {
    private static List<Person> persons = Arrays.asList(new Person("Joe", 12),
            new Person("Jim", 19), new Person("John", 21));
    public static void main(String[] args) throws Exception {
        // testStreamAPI();
        // testStreamMap();
        // testStreamPerformance();
        testInt(2, 3, 4, 2, 3, 5, 1);
        testOccurrence(2, 3, 4, 2, 3, 5, 1);
        distinctSum(2, 3, 4, 2, 3, 5, 1);
        testNestLambda();
    }

    public static void testStreamAPI() {
        // 打印年齡大于12的人
        System.out.println("使用順序流串行打印");
        persons.stream().filter(p -> p.getAge() > 12)
                .collect(Collectors.toCollection(ArrayList::new))
                .forEach(p -> {
                    System.out.println(p);
                });
        System.out.println("使用并行流并行打印,即利用多核技術可將大數據通過多核并行處理");
        persons.parallelStream().filter(p -> p.getAge() > 12)
                .collect(Collectors.toCollection(ArrayList::new))
                .forEach(p -> {
                    System.out.println(p);
                });
    }

    public static void testStreamMap() {
        // 應該用filter過濾,然后再使用map進行轉換
        persons.parallelStream().map(p -> {
            if (p.getAge() > 18)
                return p;
            return null;
        }).collect(Collectors.toCollection(ArrayList::new)).forEach(p -> {
            if (p != null)
                System.out.println(p);
        });

        persons.parallelStream().filter(p -> p.getAge() > 18)
                .map(p -> new Adult(p))
                .collect(Collectors.toCollection(ArrayList::new))
                .forEach(p -> {
                    if (p != null)
                        System.out.println(p);
                });
    }

    public static void testStreamReduce() {
        persons.parallelStream().filter(p -> p.getAge() > 18)
                .map(p -> new Adult(p))
                .collect(Collectors.toCollection(ArrayList::new))
                .forEach(p -> {
                    if (p != null)
                        System.out.println(p);
                });
    }

    public static void testStreamPerformance() {
        // 初始化一個范圍100萬整數流,求能被2整除的數字,toArray()是終點方法
        long start1 = System.nanoTime();
        int a[] = IntStream.range(0, 1_000_000).filter(p -> p % 2 == 0)
                .toArray();
        System.out.printf("測試順序流的性能: %.2fs",
                (System.nanoTime() - start1) * 1e-9);

        long start2 = System.nanoTime();
        int b[] = IntStream.range(0, 1_000_000).parallel()
                .filter(p -> p % 2 == 0).toArray();
        System.out.printf(" 測試并行流的性能: %.2fs",
                (System.nanoTime() - start2) * 1e-9);
        // 本機的測試結果是:測試順序流的性能: 0.02s 測試并行流的性能: 0.01s
        // 在100萬時,并發流快些,1000萬,并發流反而會慢些,估計和線程的頻繁切換有關(本機是8線程CPU)
    }

    public static void testInt(Integer... numbers) {
        List<Integer> l = Arrays.asList(numbers);
        List<Integer> r = l.stream()
                .map(e -> new Integer(e))
                .filter(e -> e > 2)
                .distinct()
                .collect(Collectors.toList());
        System.out.println("testInt result is: " + r);
    }

    public static void testOccurrence(Integer... numbers) {
        List<Integer> l = Arrays.asList(numbers);
        Map<Integer, Integer> r = l
                .stream()
                .map(e -> new Integer(e))
                .collect(
                        Collectors.groupingBy(p -> p,
                                Collectors.summingInt(p -> 1)));
        System.out.println("testOccurrence result is: " + r);
    }

    public static void distinctSum(Integer... numbers) {
        List<Integer> l = Arrays.asList(numbers);
        int sum = l.stream()
                .map(e -> new Integer(e))
                .distinct()
                .reduce(0, (x, y) -> x + y); // equivalent to .sum()
        System.out.println("distinctSum result is: " + sum);
    }

    public static void testNestLambda() throws Exception{
         Callable<Runnable> c1 = () -> () -> { System.out.println("Nested lambda"); };
         c1.call().run();
        // 用在條件表達式中
        Callable<Integer> c2 = false ? (() -> 42) : (() -> 24);
        System.out.println(c2.call());
    }
}

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        System.out.println(name);
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "name:" + name + " age:" + age;
    }
}

class Adult extends Person {
    public Adult(Person p) {
        super(p.getName(), p.getAge());
    }
}



 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!