美文网首页
Java8:lambda表达式

Java8:lambda表达式

作者: Jokerone_ | 来源:发表于2017-06-30 17:55 被阅读0次

    基本定义:

    简单理解:lambda表达式相当于创建的一个匿名对象.
    每个lambda表达式都与一个特定的Java接口相关联,同时这个函数式接口只能包含一个抽象方法声明。可以在接口上添加@FunctionalInterface注解注明此接口为lambda表达式相关接口。
    例如:
    声明一个函数式接口Converter:

    @FunctionalInterface
    public interface Converter<F, T> {
    
        T convertor(F from);
    
    //    T convertor2(F from);
    
    }
    

    通过lambda表达式生成一个Converter类型的对象:

            Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
            Integer converted = converter.convertor("8");
            System.out.println(converted);
    

    Java8允许通过 :: 关键字传递方法或者构造函数的引用。

    传递静态方法

    例如:

            Converter<String, Integer> staticFunConverter = Integer::valueOf;
            Integer staticFunConverted = staticFunConverter.convertor("123");
            System.out.println(converted);   // 123
    

    传递对象方法

    例如:
    新建一个类Something

     static class Something {
            String startsWith(String s) {
                return String.valueOf(s.charAt(0));
            }
        }
    
    

    通过:: 关键字引用Something类型对象的方法:

        Something something = new Something();
            Converter<String, String> objFunConverter = something::startsWith;
            String objFunConverted = objFunConverter.convertor("Java");
            System.out.println(objFunConverted);//J
    

    传递构造方法

    先定义一个多构造函数的bean

        static class Person {
            String firstName;
            String lastName;
    
            Person() {
            }
    
            Person(String firstName, String lastName) {
                this.firstName = firstName;
                this.lastName = lastName;
            }
        }
    

    然后定义一个可以创建person 的personFacotory接口

    public interface PersonFactory<P extends FuncTest.Person> {
    
        P create(String firstName, String lastName);
    
    }
    

    通过构造函数引用,实现工厂接口。

    PersonFactory<Person> personFactory = Person::new;
            Person person = personFactory.create("pee", "hao");
            System.out.println(person.lastName);//hao
    
    

    上面的例子中通过Person::new创建一个Person类构造函数的引用,Java编译器会根据PersonFactory.create方法的签名自动选择合适的构造函数。

    lambda表达式访问变量范围

    lambda表达式访问变量范围和匿名内部类相同。

    可访问本地final局部变量。

    final int num = 1;
    Converter<Integer, String> stringConverter =
            (from) -> String.valueOf(from + num);
    
    stringConverter.convert(2);     // 3
    

    可访问成员变量和静态变量。

    lambda表达式不能访问默认接口方法。

    带有扩展方法的接口

    public interface Formula {
    
        double calculate(int a);
    
        //JAVA8中可以通过default关键字在接口中添加一个非抽象方法(即:扩展方法)
        default double sqrt(int a) {
            return Math.sqrt(a);
        }
    
    }
    
    Formula formula = new Formula() {
        @Override
        public double calculate(int a) {
            return sqrt(a);
        }
    };
    System.out.println(formula.calculate(9));
    System.out.println(formula.sqrt(9));
    
    Formula formula1 = Math::sqrt;//此处无法通过直接调用扩展方法sqrt实现。
    

    Java8内置的函数式接口

    已经存在的Comparator接口和Runnable接口。

    Predicates(断言)

    Predicates是包含一个参数返回值为boolean型的函数,为了满足复杂的逻辑条件(or and negate)Predicates接口包含各种默认方法。

     //Predicates(断言)
            Predicate<String> predicate = (s -> s.length() > 0);
            System.out.println(predicate.test("foo"));//true
            System.out.println(predicate.negate().test("foo"));//false
    

    Functions

    Functions接口接收一个参数并生成一个结果,可以使用Functions接口的默认方法(compose, andThen)将多个functions串联起来。
    注:compose, andThen的区别,compose方法自身先执行,再把执行结果传递给调用者执行。andThen先执行前一步的调用者,获取前一步执行结果后再执行。

    //Functions
            Function<Integer, Integer> time2 = e -> e * 2;
            Function<Integer, Integer> squared = e -> e * e;
    
            System.out.println(time2.compose(squared).apply(4));//32
            System.out.println(time2.andThen(squared).apply(4));//64
    

    Consumers

    Consumers接口表示在单个输入参数上要执行的操作。

    //Consumers
            Consumer<FuncTest.Person> greeter = person -> System.out.println("hello :" + person.firstName);
            greeter.accept(new FuncTest.Person("jiahong", "hao"));//hello:jiahong
    

    Comparators

    Optionals

    Optionals不是一个函数式接口,而是一个用来防止出现NullPointerException异常的很好工具。

    Streams

    java.util.Stream表示可以执行一个或多个操作的序列元素,stream的操作可以分为两种类型:中间操作或最终操作。中间操作的返回值还是一个stream,最终操作的返回值是一个确定的类型。可以通过链式调用可以将多个中间操作串联起来。stream上的操作既可以串行执行也可以是并行执行。

    List<String> stringCollection = new ArrayList<>();
    stringCollection.add("ddd2");
    stringCollection.add("aaa2");
    stringCollection.add("bbb1");
    stringCollection.add("aaa1");
    stringCollection.add("bbb3");
    stringCollection.add("ccc");
    stringCollection.add("bbb2");
    stringCollection.add("ddd1");
    
    Filter

    Filter通过断言来对stream中所有的元素进行判断,由于filter是一个中间操作,因此我们可以在filter后面再调用forEach操作,ForEach操作接收一个consumer接口作为参数,这个consumer会在所有通过filter的元素上执行。ForEach是一个最终操作,返回值类型是void,因此我们不能在ForEach后面再调用其他操作。(最终操作是回去执行的操作。这里的forEach相当于for(...)循环了)

            stringCollection
                    .stream()   
                    .filter(s -> s.startsWith("a"))
                    .forEach(System.out::println);
            //aaa2 aaa1
    
    Sorted

    Sorted是一个中间操作,sorted操作接收Comparator接口作为参数,返回一个有序的stream。如果传递一个空函数给sorted,会按照自然顺序进行排序,如果元素不支持排序,会在最终操作时抛出异常。

    stringCollection
        .stream()
        .sorted()
        .filter((s) -> s.startsWith("a"))
        .forEach(System.out::println);
    
    // aaa1 aaa2
    
    Map

    map是一个中间操作,通过给定的转换方法map能将stream中的每个元素映射为另外一种对象,下面的例子中将每个string转换为upper-cased string。map也支持不同类型的映射,具体映射的目标类型取决于映射方法的返回值类型。

    stringCollection
        .stream()
        .map(String::toUpperCase)
        .sorted((a, b) -> b.compareTo(a))
        .forEach(System.out::println);
    
    // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
    
    Match

    matching是一个最终操作,返回值类型为boolean型,可以用来确定某种断言是否与stream匹配。

    boolean anyStartsWithA =
        stringCollection
            .stream()
            .anyMatch((s) -> s.startsWith("a"));
    
    System.out.println(anyStartsWithA);      // true
    
    boolean allStartsWithA =
        stringCollection
            .stream()
            .allMatch((s) -> s.startsWith("a"));
    
    System.out.println(allStartsWithA);      // false
    
    boolean noneStartsWithZ =
        stringCollection
            .stream()
            .noneMatch((s) -> s.startsWith("z"));
    
    System.out.println(noneStartsWithZ);      // true
    
    Count

    Count是一个最终操作,返回stream中元素的个数,返回值类型为long。

    long startsWithB =
        stringCollection
            .stream()
            .filter((s) -> s.startsWith("b"))
            .count();
    
    System.out.println(startsWithB);    // 3
    
    Reduce

    Reduce是一个最终操作,通过给定的方法在stream上执行reduce操作,返回值为使用Optional接口包装的reduce值。(Reduce含义:把...归纳为,减少)

    Optional<String> reduced =
        stringCollection
            .stream()
            .sorted()
            .reduce((s1, s2) -> s1 + "#" + s2);
    
    reduced.ifPresent(System.out::println);
    // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
    

    Parallel Streams

    Parallel Streams能在多个线程上并发地执行,下面的例子中通过简单的使用parallel streams就能大幅度地提高系统性能。
    首先我们创建一个很大的元素唯一的list.

    int max = 1000000;
    List<String> values = new ArrayList<>(max);
    for (int i = 0; i < max; i++) {
        UUID uuid = UUID.randomUUID();
        values.add(uuid.toString());
    }
    

    我们开始测试在这个stream上执行排序所需的时间
    Sequential Sort

    long t0 = System.nanoTime();
    
    long count = values.stream().sorted().count();
    System.out.println(count);
    
    long t1 = System.nanoTime();
    
    long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
    System.out.println(String.format("sequential sort took: %d ms", millis));
    
    // sequential sort took: 899 ms
    
    

    Parallel Sort

    long t0 = System.nanoTime();
    
    long count = values.parallelStream().sorted().count();
    System.out.println(count);
    
    long t1 = System.nanoTime();
    
    long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
    System.out.println(String.format("parallel sort took: %d ms", millis));
    
    // parallel sort took: 472 ms
    
    

    上面的两段代码几乎相同,但是parallel sort的速度几乎比sequential Sort速度快50%。

    Map

    Map<Integer, String> map = new HashMap<>();
            for (int i = 0; i < 10; i++) {
                    //当键值在map中不存在时,才会执行put操作.
                map.putIfAbsent(i, "val" + i);
            }
    
            map.forEach((key, value) -> System.out.println(value));
    
            map.computeIfPresent(3, (num, val) -> val + num + 1);
            System.out.println(map.get(3)); // val331 函数式接口返回值与原来的value不同,则赋予新值.
    
            map.computeIfPresent(9, (num, val) -> null);
            System.out.println(map.containsKey(9));//false 函数式接口返回值为null,则相应的mapping会被删除
    
    
            //当键值在map中不存在时,才会计算函数式接口的值,并设值.
            map.computeIfAbsent(23, num -> "val" + num);
            System.out.println(map.containsKey(23));
            System.out.println(map.get(23));
    
            map.computeIfAbsent(3, num -> null);
            System.out.println(map.get(3));//val331
    
            map.computeIfAbsent(3, num -> "bam");
            System.out.println(map.get(3));//val331
    

    下面的例子展示了map如何删除value不为空的key的操作。

    map.remove(3, "val3");
    map.get(3);             // val33
    
    map.remove(3, "val33");
    map.get(3);             // null
    Another helpful method:
    
    System.out.println(map.getOrDefault(42, "not found"));  // not found
    System.out.println(map.get(42));//null
    
    //Merging entries of a map is quite easy:
    map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
    map.get(9);             // val9
    
    map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
    map.get(9);             // val9concat
    

    merge方法在key不存在时,可以向map中添加key/value,当key存在时,可以对这个key对应的value执行merge操作。

    todo:stream()方法中的collect()需要总结下,涉及到了经常要用到的list装换map

    JAVA8 教程
    Java8 lambda表达式10个示例
    【译】Java 8的新特性—终极版(杜琪)
    Java8初体验(二)Stream语法详解
    Java8 新特性之流式数据处理

    相关文章

      网友评论

          本文标题:Java8:lambda表达式

          本文链接:https://www.haomeiwen.com/subject/lpmaqxtx.html