Java8-03-Lambda表达式和stream

本文介绍了Java8中的函数式接口概念,包括Lambda表达式的语法和使用,如Consumer、Supplier、Function和Predicate接口。同时,详细阐述了StreamAPI的运用,如filter、map、flatMap、reduce等操作,以及方法引用和构造器引用。文章展示了如何通过这些特性进行数据处理和集合操作。
摘要由CSDN通过智能技术生成

1-先说函数式接口

1.8开始新定义了一个注解FunctionalInterface(翻译过来就是:函数式接口)

* Conceptually, a functional interface has exactly one abstract method.(摘自jdk源码说明)

接口有且仅有一个抽象的方法,可以加上注解FunctionalInterface,比如我们经常使用的Runnable接口;

接口现在还可以拥有默认方法(即在类没有对方法进行实现时,其主体为方法提供默认实现的方法)。

哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口

当然我们也可以自己定义函数式接口;只需要写一个接口,接口里面一个抽象方法(接口的方法默认都是抽象的),就可以在接口上加上@FunctionalInterface,标记为函数式接口...

2-Lambda表达式的语法

Lambda的基本语法是

(parameters) -> expression

或(请注意语句的花括号)

(parameters) -> { statements; }

Lambda表达式在Java语言中引入了 “->” 操作符, “->” 操作符被称为Lambda表达式的操作符或者箭头操作符,它将Lambda表达式分为两部分:

  • 左侧部分指定了Lambda表达式需要的所有参数。

Lambda表达式本质上是对接口的实现,Lambda表达式的参数列表本质上对应着接口中方法的参数列表。

  • 右侧部分指定了Lambda体,即Lambda表达式要执行的功能。

Lambda体本质上就是接口方法具体实现的功能。

Lambda只需要一个参数时,参数的小括号可以省略;

当Lambda体只有一条语句时,return和大括号可以省略;

Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器能够通过上下文推断出数据类型,这就是“类型推断”。

3-Java8核心的函数式接口

3.1-Consumer消费型接口

Consumer接口是消费性接口,接受一个参数,无返回值。

   public static void testConsumer() {
        testConsumerChild("abc", x-> System.out.println(x));
    }

    public static void  testConsumerChild(String str,Consumer<String> consumer){
        consumer.accept(str);
    };

3.2-Supplier供给型接口

Supplier接口是供给型接口,有返回值

public static void testSupplier() {
        List<String> list = testSupplierChild(3, () -> UUID.randomUUID().toString().replace("-", ""));
//遍历集合
        list.stream().forEach(System.out::println);
    }

    
public static List<String> testSupplierChild(int num, Supplier<String> supplier){
        List<String> list = new ArrayList<>();
        for(int i = 0; i < num; i++){
            list.add(supplier.get());
        }
        return list;
}

3.3-Function<T, R>函数式接口

Function接口是函数型接口,一个入参,一个出参,有返回值

   public static void testFunction() {
        Integer function = testFunctionChild("abc", aaa -> aaa.length());
        System.out.println(function);
    }
    public static Integer testFunctionChild(String str, Function<String, Integer> function) {
        return function.apply(str);
    }

3.4-Predicate断言型接口

Predicate接口是断言型接口,接受一个参数,返回值类型为boolean

   public static void testPredicate() {
        List<String> list = Arrays.asList("abc", "abcdef", "abcdjkl", "ycmy", "abcdjklmn");
        List<String> strList = testPredicateChild(list, s -> s.length() >= 5);
        strList.stream().forEach(System.out::println);
    }

    public static List<String> testPredicateChild(List<String> list, Predicate<String> predicate) {
        List<String> strList = new ArrayList<>();
        list.stream().forEach(aaa->{
            if(predicate.test(aaa)){
                strList.add(aaa);
            }
        });
        return strList;
    }

List 类型抽象化

public static <T> List<T> filter(List<T> list, Predicate<T> p){
    List<T> result = new ArrayList<>();
    for(T e: list){
        if(p.test(e)){
            result.add(e);
        }
    }
    return result;
}

4-方法引用与构造器引用

4.1-方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!这里需要注意的是:实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!

如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。

方法引用就是操作符“::”将方法名和对象或类的名字分隔开来。

对象::实例方法

类::静态方法

类::实例方法

比如:

(x) -> System.out.println(x); 等价于:System.out::println

(str, i) -> str.substring(i) 等价于: String::substring

() -> Thread.currentThread().dumpStack() 等价于: Thread.currentThread()::dumpStack

4.2-构造器引用

与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!

ClassName::new

Supplier<Apple> c1 = Apple::new;等价于Supplier<Apple> c1 = () -> new Apple();

Function<Integer, Apple> c2 = Apple::new;

等价于

Function<Integer, Apple> c2 = (weight) -> new Apple(weight);

5-Stream

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

注意: Stream 自己不会存储元素。 Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

创建流(数据源) ---->中间操作 ---->终止操作(终端操作)

5.1-中间操作之筛选与切片

filter,distinct,limit,skip操作

   private static void test001() {
        List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
        numbers.stream()
                .filter(i -> i % 2 == 0)  //获取偶数
                .distinct()  //去重复
                .forEach(System.out::println); //遍历
    }

流支持limit(n)方法,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递给limit。

流还支持skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。请注意, limit(n)和skip(n)是互补的!

private static void test002() {
        List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4,6,8,10);
        numbers.stream()
                .filter(i -> i % 2 == 0)  //获取偶数  2-2-4-6-8-10
                .distinct()  //去重复 2-4-6-8-10
                .skip(2) //6-8-10
                .limit(2) //6-8
                .forEach(System.out::println); //遍历  6-8
    }

5.2-映射map和flatMap

   private static void test003() {
        List<String> words = Arrays.asList("Java8", "Lambdas", "In", "Action");
         words.stream()
                .map(String::length)
                .collect(Collectors.toList())
                .forEach(System.out::println);
    }

如果你要找出每道菜的名称有多长,怎么做?你可以像下面这样,再链接上一个map:

需求:

对于一张单词 表 , 如 何 返 回 一 张 列 表 , 列 出 里 面 各 不 相 同 的 字 符 呢 ? 例 如 , 给 定 单 词 列 表["Hello","World"],你想要返回列表["H","e","l", "o","W","r","d"]。

  private static void test005() {
        String[] arrayOfWords = {"Hello", "World"};
        Stream<String> words = Arrays.stream(arrayOfWords);
        words.map(word -> word.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.toList())
                .forEach(System.out::println);
    }

使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容

给定两个数字列表,如何返回所有的数对呢?例如,给定列表[1, 2, 3]和列表[3, 4],应该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]。为简单起见,你可以用有两个元素的数组来代表数对。

  private static void test006() {
        List<Integer> numbers1 = Arrays.asList(1, 2, 3);
        List<Integer> numbers2 = Arrays.asList(3, 4);
        List<int[]> pairs = numbers1.stream()
                        .flatMap(i -> numbers2.stream()
                                .map(j -> new int[]{i, j})
                        )
                        .collect(Collectors.toList());
        pairs.forEach( abc ->{
            System.out.println(Arrays.toString(abc));
        });
    }

如何扩展前一个例子,只返回总和能被3整除的数对呢?例如(2, 4)和(3, 3)是可以的

   private static void test007() {
        List<Integer> numbers1 = Arrays.asList(1, 2, 3);
        List<Integer> numbers2 = Arrays.asList(3, 4);
        List<int[]> pairs = numbers1.stream()
                .flatMap(i -> numbers2.stream().filter(j ->(i+j) %3==0)
                        .map(j -> new int[]{i, j})
                )
                .collect(Collectors.toList());
        pairs.forEach( abc ->{
            System.out.println(Arrays.toString(abc));
        });
    }

5.3-查找和匹配

Stream API通过allMatch、 anyMatch、 noneMatch、 findFirst和findAny方法提供了这样的工具。

通过单词就知道对应的含义,demo省略...

   private static void test008() {
        List<String> words = Arrays.asList("Java8", "Lambdas", "In", "Action");
        Optional<String> optional = words.stream()
                .filter(abc -> abc.length() > 30)
                .findAny();
//        System.out.println(optional.orElse("580"));//返回580
        System.out.println(optional.isPresent()?optional.get():"590");
    }

5.4-归约

Stream API通过reduce方法提供了这样的工具。

    private static void test009() {
        List<Integer> nums1 = Arrays.asList(1, 2, 3, 4);
        Integer reduce1 = nums1.stream().reduce(0, (a, b) -> a + b);
        System.out.println(reduce1);

        List<Integer> nums2 = Arrays.asList(1, 2, 3, 4);
        Integer reduce2 = nums2.stream().reduce(1, (a, b) -> a*b);
        System.out.println(reduce2);

        List<Integer> nums3 = Arrays.asList(1, 2, 3, 4,5,8,12,6,9,7);
        Optional<Integer> reduce3 = nums3.stream().reduce(Integer::max);
        System.out.println(reduce3.orElse(-1));

        Optional<Integer> reduce4 = nums3.stream().reduce(Integer::min);
        System.out.println(reduce4.orElse(-1));
    }

5.5-操作小结

5.6-其他

Collectors.summingInt;

Collectors.averagingInt

//平均值

double avgCalories =menu.stream().collect(Collectors.averagingInt(Dish::getCalories));

IntSummaryStatistics menuStatistics =menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));

这个收集器会把所有这些信息收集到一个叫作IntSummaryStatistics的类里,它提供了方便的取值( getter)方法来访问结果。打印menuStatisticobject会得到以下输出:IntSummaryStatistics{count=9, sum=4300, min=120,average=477.777778, max=800}

同样,相应的summarizingLong和summarizingDouble工厂方法有相关的LongSummaryStatistics和DoubleSummaryStatistics类 型 , 适 用 于 收 集 的 属 性 是 原 始 类 型long或double的情况

groupingBy-分组

Map<Dish.Type, List<Dish>> dishesByType =menu.stream().collect(groupingBy(Dish::getType));

partitioningBy-分区

Map<Boolean, List<Dish>> partitionedMenu =

menu.stream().collect(partitioningBy(Dish::isVegetarian));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值