lambda表达式和Stream流式编程

学习目标

  1. lambda表达式
  2. 流式编程

学习内容

lambda表达式

– lambda表达式是实现接口的一种方式
– 必须是接口,且接口中有且只有一个方法
– 格式:(参数) -> {主体}
– lambda表达式无需关注其他内容,只看被重写方法的参数列表和方法体即可

package com.haohao.learn;

import java.util.function.IntPredicate;

public class Hello {
    public static void main(String[] args) {
    	//lambdao表达式格式
        printNum((int value) -> {
            return value % 2 == 0;
        });
		//常规匿名内部类格式
        printNum(new IntPredicate() {
            @Override
            public boolean test(int value) {
                return value % 2 == 0;
            }
        });
    }

	//IntPredicate是一个接口,有且只有一个test方法,符合lambda表达式要求
    public static void printNum(IntPredicate predicate){
        int[] arr = {1,2,3,4,5,6,7,8,9,10};
        for (int a : arr) {
            if (predicate.test(a)){
                System.out.println(a);
            }
        }
    }
}

  • lambda表达式省略规则
    – 参数类型可省略
    – 方法体只有一行代码时,大括号,return,和唯一一句代码的分号可省略
    – 方法体只有一个参数时小括号可省略

Stream流

jdk8的Stream使用的是函数式编程模式,可以用来对集合或者数组进行链状流式的操作,使得对数组或集合的操作更方便

public class Author {

    public static void main(String[] args) {

        ArrayList<String> strings = new ArrayList<>();
        strings.add("艾瑞莉娅");
        strings.add("瑞文");
        strings.add("菲奥娜");
        strings.add("丽桑卓");
        strings.add("薇恩");
        strings.add("薇恩");
        //过滤
        //遍历
        strings.stream()//转换成Stream流
                .distinct()//去重
                .filter(s -> s.length() < 4)//过滤
                .forEach(s -> System.out.println(s));//遍历

    }

}
常用操作
  • 创建流
    – 单列集合:集合对象.stream()
		ArrayList<String> strings = new ArrayList<>();
		Stream<String> stream = strings.stream();

– 数组:Arrays.stream(数组)/Stream.of(数组)

        Integer[] ints = {1,2,3,4};
        Stream<Integer> stream1 = Arrays.stream(ints);
        Stream<Integer> stream2 = Stream.of(ints);

– 双列集合:

        HashMap<String, Integer> map = new HashMap<>();
        map.put("易",11);
        map.put("赵信",15);
        map.put("泰达米尔",25);
        //Set> entries = map.entrySet();
        //Stream> stream = entries.stream();
       Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream(); 
  • 中间操作
        //前置
        ArrayList<String> strings = new ArrayList<>();
        Stream<String> stream = strings.stream();
        strings.add("艾瑞莉娅");
        strings.add("瑞文");
        strings.add("菲奥娜");
        strings.add("丽桑卓");
        strings.add("薇恩");
        strings.add("薇恩");

– filter:对流中的元素进行条件过滤,符合条件的才能继续留在流中

		strings.stream()//转换成Stream流
               .filter(s -> s.length() < 4)//过滤
               .forEach(s -> System.out.println(s));//遍历

– map:对流中的元素进行计算或元素类型的转换

        strings.stream()
                .map(s -> s.length())//将元素转为int类型
                .forEach(integer -> System.out.println(integer));

– distinct:去除流中的重复元素,依赖的是Object的equals来判断是否是相同对象的,所以要重写equals方法

		strings.stream()
                .distinct()
                .forEach(s -> System.out.println(s));

– sorted:对流中的元素进行排序,用到Comparable相关,实体类需实现Comparable接口,重写compareTo方法,定义比较规则234567890-=

		strings.stream()
                .sorted()
                .forEach(s -> System.out.println(s));

– limit:设置流最大长度,超出部分被抛弃

        strings.stream()
                .limit(2)
                .forEach(s -> System.out.println(s));

– skip:跳过流中的前n和元素,返回剩下的元素

        strings.stream()
                .skip(2)
                .forEach(s -> System.out.println(s));

– flatMap:map只能把一个对象转换成另一个对象来作为流中的元素,而faltMap可以把一个对象转换成多个对象作为流中的元素

        ArrayList<String> strings = new ArrayList<>();
        strings.add("艾/瑞/莉/娅");
        strings.add("瑞/文");
        strings.add("菲/奥/娜");
        strings.add("丽/桑/卓");
        strings.add("薇/恩");
        strings.add("薇/恩");
        strings.stream()
                .flatMap(s -> Arrays.stream(s.split("/")))
                .forEach(s -> System.out.println(s));
  • 终结操作
    – forEach:对流中的元素进行遍历操作
    – count:获取流中当前元素的个数
    – max&min:获取流中元素的最值
    – conllect:把当前流当中的元素转换成一个集合,一般用工具类(java.util.stream.Collectors),该工具类中有很多类似 toList,toSet等方法,注意toMap方法需传入两个参数,指定map集合的key和value
ArrayList<String> strings = new ArrayList<>();
        strings.add("艾/瑞/莉/娅");
        strings.add("瑞/文");
        strings.add("菲/奥/娜");
        strings.add("丽/桑/卓");
        strings.add("薇/恩");
        strings.add("薇/恩");
        List<String> collect = strings.stream()
                .flatMap(s -> Arrays.stream(s.split("/")))
                .collect(Collectors.toList());
        System.out.println(collect);

– 查找与匹配

  • anyMatch:判断是否存在符合规则的元素,任意存在一个元素则返回true,否则返回false
  • allMatch:判断流中的元素是否全部符合指定规则,全部符合返回true,否则返回false
  • noneMatch:判断流中的元素是否全部不符合指定规则,全部不符合则返回true,否则返回false
  • findAny:随机获取流中的一个元素
  • findFirst:获取流中的第一个元素
    – reduce归并:对流中的数据按照你指定的计算方法计算出一个结果。(缩减操作)
    可以把stream流中的元素组合起来,当传入一个初始值的时候,会按照我们的计算方式(accumulator方法自定义)依次拿流中的元素在初始值的基础上进行计算,计算结果再和后面的元素计算。
    其内部计算方法为:其中identity就是我们可以通过方法参数传入的初始值,accumulator的apply具体进行什么计算也是我们通过方法参数来确定的。
T result = identity;//identity是自己赋的初始值,类型和Stream流中的元素类型一致
for(T element : this stream)//遍历Stream流中的每一个元素
	result = accumulator.apply(result,element)//在这里指定计算逻辑并将结果返回给result
return result;
        ArrayList<String> strings = new ArrayList<>();
        strings.add("瑞文");
        strings.add("艾瑞莉娅");
        strings.add("菲奥娜");
        strings.add("丽桑卓");
        strings.add("薇恩");
        strings.add("薇恩");
        Integer reduce = strings.stream()
                .map(s -> s.length())
                .reduce(0, (i, element) -> i + element);//第一个参数是初始值,第二个参数是lambda表达式,定义了如何计算
        System.out.println(reduce);
注意事项
  • 惰性操作:若没有终结操作,则中间操作不会执行
  • 流是一次性的,一旦经过终结操作,这个流就不能被再次使用
  • 不会影像原数据,在流操作之后,元数据不变

Optional

  • 概述:optional主要是为了优雅的避免空指针异常
  • 使用:Optional类似包装类,将具体数据封装在Optional对象内部,然后直接使用Optional中封装好的方法操作数据
    – 创建optional对象
//一般使用Optional的静态方法:ofNullable即可直接将数据封装为Optiona对象,该方法不论传入的值是否为空都没问题
Optional<String> opt = Optional.ofNullable("aaa");
//若确定传入的参数不为空,也可使用Optional的静态方法:of来封装对象
Optional<String> opt = Optional.of("aaa");

– 安全消费值:一般使用Optional对象的ifPresent方法来消费其值,这个方法会判断内部封装的数据是否为空,不为空时才会执行具体的消费代码
– 安全获取值:Optional的get方法,但是可能有异常,推荐使用orElseGet/orElseThrow

//orElseGet:当数据不为空,则获取该数据,如果数据为空,则根据传入的参数来创建对象作为默认值返回
Optional<ArrayList<String>> strings1 = Optional.ofNullable(strings);//将列表转为Optional类型的对象
try {
    strings1.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("数据为null"))
    } catch (Throwable throwable) {
          throwable.printStackTrace();
	}

//orElseThrow:获取数据,若数据不为空则获取数据,若为空则根据传入的参数来创建异常,抛出

– 过滤:filter方法对数据进行过滤,类似中间操作的filter方法
– 判断:建议使用ifPresent()方法
– 数据转换:map方法可以进行类型转换,类似中间操作的map方法

函数式接口

概述
  • 只有一个抽象方法的接口,成为函数接口,JDK的函数式接口都有注解:@FunctionalInterface,但即使没有该注解,但是接口只有一个抽象方法,也是函数式接口。
常见函数式接口
  • Consumer:消费接口:根据参数列表和返回值类型可知,在方法中对传入的参数进行消费
  • Function:计算转换接口,在方法中对传入的参数计算或转换,把结果返回
  • Predicate:判断接口:对方法中传入的参数条件判断,返回判断结果
常用的默认方法
  • and方法 相当于用&&拼接
  • or方法 相当于用||拼接
  • negate 相当于在判断条件前加了个“!”,取反

方法引用

推荐用法

在使用lambda表达式时,若方法体中只有一个方法调用(直接引用其他方法)的话,可以用方法引用进一步简化代码

基本格式

类名/对象名::方法名

高级用法

基本数据类型优化

Stream提供了方法可以直接将包装类转为基本数据类型,如:mapToInt,mapToLong,mapToDouble,flatMapToDouble等

并行流

当流中有大量元素时,我们可以使用并行流去提高操作效率,实质上并行流就是把任务分配给多个线程完成,自己代码实现比较麻烦复杂,使用Stream的话,只需修改一个方法的调用即可使用并行流实现

  • parallel方法将流转换成并行流
  • parallelStream方法将集合直接转换成并行流
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer res;
        integerStream.parallel()//调用parallel方法即可将流转换成并行流
                .peek(num -> System.out.println(num + " -> " + Thread.currentThread().getName()))//peek方法是调试方法

你可能感兴趣的:(java)