Lambda表达式介绍和Stream API解析

Lambda表达式介绍和Stream API解析

1. lambda表达式

百度百科:
Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包。
Lambda 表达式是Java8推出的重要的特性之一(java8新特性介绍),允许把函数作为一个方法的参数传入,是面向函数式编程的思想,一定程度上可以使代码更简洁,它的本质就是一个语法糖

传统方式创建线程
		new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("传统方式创建线程");
            }
        }).start();
使用lambda方式创建线程
new Thread(() -> System.out.println("lambda方式创建线程")).start();

lanmbda看起来非常简洁易懂,lambda还能够推断出类型是Runable,很神奇吧。
lambda表达式语法分为三部分:1)方法输入 2)箭头函数 3)方法体

(parameters) -> expression

(parameters) ->{
    
     statements; }

2. 函数式接口讲解

通过代码简单认识lambda,我们看下java8是让Runable支持lambda的。
查看Runable的源码,看到接口上定义了一个@FunctionalInterface注解

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

查看@FunctionalInterface注解,最主要的是注释,下面代码片段已经把英文的意思进行了总结。

package java.lang;
import java.lang.annotation.*;
/** 
1)该注解只能标记在"有且仅有一个抽象方法"的接口上。
2)JDK8接口中的静态方法和默认方法,都不算是抽象方法。
3)接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。
4)该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。
5)1.8及以后版本开始支持
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {
    
    }

已经了解接口如何支持lambda表达式,现在自己定义一个函数式接口尝试下。

public class LambdaDemo {
    
    
    /**定义函数式接口*/
    public interface ImLambdaInterface {
    
    
        void print();
    }
    /**定义执行方法*/
    void testLambda(ImLambdaInterface demo) {
    
    
        demo.print();
    }
    public static void main(String[] args) {
    
    
        LambdaDemo demo = new LambdaDemo();
        demo.testLambda(() -> System.out.println("你好,我在测试函数式接口"));
    }
}

3. jdk自带函数式接口介绍

3.1 Function

说明:Function<T, R>支持传入一个参数T,返回一个参数R
示例:

//使用场景:按照属性进行list排序
list.sort(Comparator.comparingInt(a -> a.getAge()));
//**框架源码使用欣赏:Comparator的比较器就是传入Function进行处理
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
    
    
return (Comparator<T> & Serializable)
            (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}

3.2 BiConsumer

说明:BiConsumer<T,U>支持传入两个参数T和U,无返回值
示例:

//使用场景:经常使用的map遍历其实就是BiConsumer
map.forEach((k, v) -> {
    
    
    System.out.println("我是key" + k + ",我是Value" + v);
});
//**框架源码使用欣赏:HashMap源码直接取出key和value就是使用了BiConsumer
default void forEach(BiConsumer<? super K, ? super V> action) {
    
    
	     Objects.requireNonNull(action);
	     for (Map.Entry<K, V> entry : entrySet()) {
    
    
         	//源代码略.......
         	action.accept(k, v);
     	}
    }

3.3 Supplier

说明:Supplier,支持返回一个泛型对象。直接写简单示例代码可能比较空洞,直接看ThreadLocal源码更容易理解。
示例:

//使用场景:ThreadLocal可以直接使用Supplier
private static final ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> "初始值");
//**框架源码使用欣赏:ThreadLocal源码
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
    
    
        return new SuppliedThreadLocal<>(supplier);
}

3.4 Predicate

说明:传入一个函数T,返回是否符合要求。Predicate在Stream有广泛的使用场景。
示例:

//使用场景:ArrayList过滤实体属性,可以使用自带的removeIf,这个就是断言
List<Person> list = Stream.of(Person.builder().name("Kobe").age(28).build(),
                Person.builder().name("Jordon").age(36).build()).collect(Collectors.toList());
list.removeIf((t) -> t.getAge() > 34);
//**框架源码使用欣赏:ArrayList.removeIf()源码
default boolean removeIf(Predicate<? super E> filter) {
    
    
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
    
    
            if (filter.test(each.next())) {
    
    
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

4.Stream介绍

	Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
	Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
	Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
	这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
	元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

Stream流过程
Stream中的操作可以分为两大类:中间操作与结束操作,中间操作只是对操作进行了记录,只有结束操作才会触发实际的计算(即惰性求值),这也是Stream在迭代大集合时高效的原因之一。中间操作又可以分为无状态(Stateless)操作与有状态(Stateful)操作,前者是指元素的处理不受之前元素的影响;后者是指该操作只有拿到所有元素之后才能继续下去。结束操作又可以分为短路与非短路操作,这个应该很好理解,前者是指遇到某些符合条件的元素就可以得到最终结果;而后者是指必须处理所有元素才能得到最终结果。

======未完成,待续

猜你喜欢

转载自blog.csdn.net/lxn1023143182/article/details/112798491