Lambda 表达式,即函数式编程是 JDK8 的一个新特性,也被称为闭包,Lambda表达式允许把函数作为一个方法的参数,即行为参数化,函数作为参数传递进方法中。

Lambda表达式可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。

总结就是:使用不可变值与函数,函数对不可变值进行处理,映射成另一个值。


一、什么是函数式接口

函数接口是只有一个抽象方法的接口,用作 Lambda 表达式的类型。使用@FunctionalInterface注解修饰的类,编译器会检测该类是否只有一个抽象方法或接口,否则,会报错。可以有多个默认方法,静态方法。

lambda 表达式是一小段代码,它接受参数并返回一个值。Lambda 表达式类似于方法,但它们不需要名称,并且可以直接在方法体中实现。

语法:最简单的 lambda 表达式包含一个参数和一个表达式:

  • 零参数:

() -> System.out.println("零参数 lambda");
  • 一个参数:

p -> System.out.println("一个参数:" + param);
  • 多个参数:

(p1 [,p2,p3,....pn]) -> System.out.println("多个参数:" + p1 + ", " + p2 + ... + pn);

上面的表达式有一定的限制。它们要么返回一个值要么执行一段方法,并且它们不能包含变量、赋值或语句,例如if or for 。为了进行更复杂的操作,可以使用带有花括号的代码块。如果 lambda 表达式需要返回一个值,那么代码块应该有一个return语句。


(parameter1, parameter2) -> { code block [return] }

1、方法引用

  • 类 :: 静态方法

Consumer<String> c = [ (s) -> System.out.println(s);  <=>  System.out::println; ]
  • 对象 :: 实例方法

List<String> list = Lists.newArrayList();
Consumer<String> c = [ (e) => list.add(e);  <=>  list::add; ]
  • 构造器 :: new

Supplier<List<String>> s = [ () -> new ArrayList<>(); <=> ArrayList::new; ]

2、@FunctionalInterface注解

有且只有一个抽象方法的接口被称为函数式接口,函数式接口适用于函数式编程的场景,Lambda就是Java中函数式编程的体现,可以使用Lambda表达式创建一个函数式接口的对象,一定要确保接口中有且只有一个抽象方法,这样Lambda才能顺利的进行推导。

与@Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface 。该注解可用于一个接口的定义上,一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法(equal和hashcode方法不算),否则将会报错。但是这个注解不是必须的,只要符合函数式接口的定义,那么这个接口就是函数式接口。

3、 java8自带的常用函数式接口。

4、简单的例子

package com.biyu.study.lambda;

import com.biyu.study.lambda.bean.Employee;

import java.math.BigDecimal;
import java.util.function.*;

public class Test {
    public static void main(String[] args) {
        Predicate<Integer> predicate = x -> x > 20;
        Employee employee = new Employee("Lily", "女", 18);
        System.out.println("Lily的大于18岁吗?:" + predicate.test(employee.getAge()));

        Consumer<String> consumer = System.out::println;
        consumer.accept("Hello World");

        Function<Employee, String> function = Employee::getName;
        String name = function.apply(employee);
        System.out.println(name);

        Supplier<Integer> supplier = () -> Integer.valueOf(BigDecimal.TEN.toString());
        System.out.println(supplier.get());

        UnaryOperator<Boolean> unaryOperator = uglily -> !uglily;
        Boolean apply2 = unaryOperator.apply(true);
        System.out.println(apply2);

        BinaryOperator<Integer> operator = (x, y) -> x * y;
        Integer integer = operator.apply(2, 3);
        System.out.println(integer);

        test(() -> "函数式接口");
    }

    /**
     * 自定义函数式接口使用
     *
     * @param worker
     */
    public static void test(Worker worker) {
        String work = worker.work();
        System.out.println(work);
    }

    public interface Worker {
        String work();
    }
}

以上演示了lambda接口的使用及自定义一个函数式接口并使用。

二、Lambda 流的常用方法

1、ForEach

集合的遍历 forEach 方法:

public static void testForEach(){
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("2");
            add("3");
        }};

        list.forEach(s-> System.out.println(s));
    }

2、Collect

将操作后的对象转化为新的对象:

public static void testCollect(){
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("2");
            add("2");
        }};

        //转换为新的list
        List newList = list.stream().map(s -> Integer.valueOf(s)).collect(Collectors.toList());
        System.out.println(newList);
    }

3、Filter

Filter 为过滤的意思,只要满足 Filter 表达式的数据就可以留下来,不满足的数据被过滤掉。

public static void testFilter() {
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("2");
            add("3");
        }};

        List<String> filtered = list.stream()
                // 过滤掉我们希望留下来的值
                // 表示我们希望字符串是 1 能留下来
                // 其他的过滤掉
                .filter(str -> "1".equals(str))
                .collect(Collectors.toList());
        System.out.println(filtered);
    }

4、Map

map 方法可以让我们进行一些流的转化,比如原来流中的元素是 A,通过 map 操作,可以使返回的流中的元素是 B。

public static void testMap() {
        List<String> list = new ArrayList<String>() {{
            add("A");
            add("B");
            add("C");
        }};
        //通过 map 方法list中元素转化成 小写
        List<String> strLowerList = list.stream()
                .map(str -> str.toLowerCase())
                .collect(Collectors.toList());
        System.out.println(strLowerList);
    }

5、MapToInt

mapToInt 方法的功能和 map 方法一样,只不过 mapToInt 返回的结果已经没有泛型,已经明确是 int 类型的流了。

public static void testMapToInt() {
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("2");
            add("3");
        }};
        List<Integer> intList=list.stream()
                .mapToInt(s->Integer.valueOf(s))
                // 一定要有 mapToObj,因为 mapToInt 返回的是 IntStream,因为已经确定是 int 类型了,所以没有泛型的,
                // 而 Collectors.toList() 强制要求有泛型的流,所以需要使用 mapToObj方法返回有泛型的流
                .mapToObj(s->s)
                .collect(Collectors.toList());
        System.out.println(intList);
        Double sum = list.stream()
                .mapToDouble(s->Double.valueOf(s))
                // DoubleStream/IntStream 有许多 sum(求和)、min(求最小值)、max(求最大值)、average(求平均值)等方法
                .sum();
        System.out.println(sum);
    }

6、Distinct

distinct 方法有去重的功能:

public static void testDistinct() {
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("2");
            add("2");
            add("5");
            add("3");
        }};
        List<Integer> intList = list.stream()
                .map(s -> Integer.valueOf(s))
                .distinct()
                .collect(Collectors.toList());
        System.out.println(intList);
    }

7、Sorted

Sorted 方法提供了排序的功能,并且允许我们自定义排序。

public static void testSorted() {
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("2");
            add("-1");
            add("9");
            add("11");
            add("27");
            add("0");
        }};
        List<Integer> intList = list.stream()
                .map(s -> Integer.valueOf(s))
                // 等同于 .sorted(Comparator.naturalOrder()) 自然排序
                .sorted()
                .collect(Collectors.toList());
        System.out.println(intList);
        // 自定义排序器
        intList =list.stream()
                .map(s -> Integer.valueOf(s))
                // 反自然排序
                .sorted(Comparator.reverseOrder())
                .collect(Collectors.toList());
        System.out.println(intList);
    }

8、groupingBy

groupingBy 是能够根据字段进行分组,toMap 是把 List 的数据格式转化成 Map 的格式。

public static void testGroupBy() {
        List<String> list = new ArrayList<String>() {{
            add("iphone 4s");
            add("iphone 6");
            add("iphone 12");
            add("iphone 14");
            add("xiaomi");
            add("huawei");
            add("oppo");
        }};

        Map<String, List<String>> strList = list.stream().collect(Collectors.groupingBy(s -> {

            if (s.startsWith("iphone")){
                return "iphone";
            } else {
                return "other";
            }
        }));
        System.out.println(strList);
    }

9、FindFirst

findFirst 表示匹配到第一个满足条件的值就返回:

public static void testFindFirst() {
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("2");
            add("2");
        }};

        list.stream()
                .filter(s -> "2".equals(s))
                .findFirst()
                .get();

        // 防止空指针
        list.stream()
                .filter(s -> "2".equals(s))
                .findFirst()
                // orElse 表示如果 findFirst 返回 null 的话,就返回 orElse 里的内容
                .orElse("3");

        Optional<String> str = list.stream()
                .filter(s -> "2".equals(s))
                .findFirst();
        // isPresent 为 true 的话,表示 value != null
        if (str.isPresent()) {
            return;
        }
    }

10、Reduce

reduce 方法允许我们在循环里面叠加计算值:

public static void testReduce() {
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("2");
            add("3");
        }};

        int sum = list.stream()
                .map(s -> Integer.valueOf(s))
                // s1 和 s2 表示循环中的前后两个数
                .reduce((s1, s2) -> s1 + s2)
                .orElse(0);
        System.out.println(sum);
        sum =list.stream()
                .map(s -> Integer.valueOf(s))
                // 第一个参数表示基数,会从 100 开始加
                .reduce(100, (s1, s2) -> s1 + s2);
        System.out.println(sum);
    }

11、Peek

peek 方法很简单,我们在 peek 方法里面做任意没有返回值的事情,比如打印日志:

public static void testPeek() {
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("2");
            add("3");
        }};
        list.stream().map(s -> Integer.valueOf(s))
                .peek(s -> System.out.println(s))
                .collect(Collectors.toList());
    }

12、Limit

limit 方法会限制输出值个数,入参是限制的个数大小:

public static void testLimit() {
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("2");
            add("3");
            add("4");
            add("5");
        }};
       List<Integer> integers= list.stream()
                .map(s -> Integer.valueOf(s))
                .limit(3L)
                .collect(Collectors.toList());
        System.out.println(integers);

    }

13、Max,Min

通过 max、min 方法,可以获取集合中最大、最小的对象。

public static void testMaxMin() {
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("3");
            add("2");
            add("-12");
            add("99");
        }};

        String maxNum = list.stream().max(Comparator.comparing(s -> Integer.valueOf(s))).get();
        String minNum = list.stream().min(Comparator.comparing(s -> Integer.valueOf(s))).get();
        System.out.println(maxNum);
        System.out.println(minNum);
    }

三、写在最后

本章节主要从实际使用讲述了常用的方法及流,文中例子主要是为了讲解较为简单,大家可以去使用java8重构自己现有的代码,自行领会lambda的奥妙使用。java8可以很清晰表达你要做什么,代码也很简洁。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐