java 8 lambda 深度讲解

前言

从19年开始接触lambda表达式,一开始比较难于看懂,但是写多了,就会发生代码简洁明了,而且还有很多高级的写法,让代码质量越来越高,下面就开始介绍一下lambda表达式的写法和一些高级写法的介绍

基础类

  • 基础函数类
  1. Function<T,R> 可以存储只有一个入参和返回值得对象方法,T表示入参,R表示返回值,看如下代码,String.valueOf(),是一个String类提供的一个静态方法,这个指向的入参是Integer,出参是String的方法。
    下面两行代码,定义了一个String对象,将对象的方法保存到function1
        //保存一个初始化string的对象静态方法
		Function<Integer, String> function = String::valueOf;
		//保存一个切割字符串的对象方法
        String s1 = "";
        Function<String,String[]> function1 = s1::split;
  1. Predicate<? super T> 可以存储一个出参是boolean的对象方法,主要Optional和Stream用于过滤方法。
		//保存一个比较字符串一直的对象方法
		String s1 = "1234";
		Predicate<String> predicate = s1::equals;

3.Supplier<? super T> 可以存储一个无参并且有返回值的方法,如下代码

        //保存一个创建String对象的方法
        Supplier<String> supplier = String::new;
        //保存一个字符串长度对象方法
        Supplier<Integer> supplier1 = "1234"::length;

4.Consumer<? super T> 只是单个入参,没有出参的方法,看如下代码

 class  User {
        String name;

        public void set(String name) {
            this.name = name;
        }
    }
 User user = new User();
 Consumer<String> consumer = user::set;

正式介绍lambda

  • 开始正式介绍lambda涉及到的使用类,总结起来分为两大类,一个是Optional和Stream
    1.Optional,主要是处理单个对象的操作
    Optional.of()不会去判断出入的对象是否为null,Optional.ofNullable()会去判断传入的对象是否为null,如果是为null,后面的操作都不会执行
//把s值打印出来
String s ="1123";
//如果s不为null就打印出来,为null的就不会被打印出来
s = Optional.ofNullable(s).ifPresent(System.out::println);
 //传入的s不可以为null,是否就报错,如果对象不为null将会打印出来
Optional.of(s).ifPresent(System.out::println);

Optional.map(Function<R,T>)主要是主要是执行具体的业务,这个方法在stream里面也存在,使用方式是一样的,如下所示

String s = "123";
//把s字符串后面拼接一个aaa的字符串,并打印出来
Optional.of(s).map(s1 -> s1+"aaa").ifPresent(System.out::println);

在这里插入图片描述
其中还存在一个方法flatMap(),这个方法是处理完毕业务,最终返回值为Optional,可以看看如下代码,目前为止我还没有看到这个方法的可用之处,知道一下就好
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020102714225842.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoaWRhcnVhbmppYW4=,size_16,color_FFFFFF,t_70#pic_center
Optional.filter(Predicate<? super T> predicate),这个起到了一个过滤的作用,如果不满足这个过滤后续的业务操作不不会进行,在Stream里面也有这个方法,使用的方式也是一样的

String s = "123";
//判断s是否等于123,如果等于就打印出123aaa,不等于就过滤掉,这个操作结束
//,s1+"aaa"这个语句自然也不会执行
Optional.of(s).filter("123"::equals).map(s1 -> s1 + "aaa").
ifPresent(System.out::println);

Optional最终的结束语方法如下所示

方法解释
ifPresent()没有返回值,主要是把值设置给某一个对象
get ()获取Optional操作的对象,对象不可以为NULL,否则会报错
orElseGet((Supplier<? extends T>)获取Optional操作对象,如果对象NULL,那就执行Supplier传入的对象方法执行获取最终的结果
orElse(T)获取Optional操作的对象,如果对象为null,那就返回T传入的对象
orElseThrow(Supplier<? extends Throwable> exceptionSupplier)获取Optional操作的对象,如果对象为NULL,实现的对象务必返回一个初始化的异常类
String s = "123";
//最终返回aaa_s1
String s1 = Optional.ofNullable(s).filter("12"::equals).map(ss -> ss + "abc").orElse("aaa_s1");
//最终返回aaa_s2
String s2 = Optional.ofNullable(s).filter("12"::equals).map(ss -> ss + "abc").orElseGet(() -> "aaa_s2");
//最终抛出一个RuntimeException异常
String s3 = Optional.ofNullable(s).filter("12"::equals).map(ss -> ss + "abc").orElseThrow(RuntimeException::new);
//最终返回123abc
String s4 = Optional.ofNullable(s).filter("123"::equals).map(ss -> ss + "abc").orElseThrow(RuntimeException::new);

2.Stream 这个是lamdba表达式核心对象,也是最为复杂经常使用到的,他是对一个集合的处理,可以看看如下方法
Optional最终的结束语方法如下所示

方法解释
Coolection.Stream(),Stream.of(T …t),这个是开始触发一个遍历集合的开始,
Stream.generate(Supplier< T > )开启无限次数的遍历,必须要结合limit(),anyMatch()或者noneMatch()相关方法来结束遍历
parallelStream()开启多线程的遍历,可以在map()方法存在多线程处理,但是需要在结束流里面将对其结果进行合并,后面会详细讲解
map()执行具体业务方法
filter(Predicate<? super T> predicate)启动过滤作用,这个是回去过滤遍历里面的单个对象,不会让整个遍历结束
peek(Consumer<? super T> action)看得出来的是一个只有入参的对象方法,这个不需要有返回值,所以可以自己把传入的对象进行修改
limit()限制遍历的次数
skip()开始遍历的位置
distinct对集合进行去重,如果是一个自定义的对象,需要去重新对象的equal方法,去重方可生效
sorted(),sorted(Comparator<? super T> comparator)对集合进行排序,启动sorted()是需要集合对象基础Comparable接口,反之会报错
anyMatch(),noneMatch(),allMatch()这个是整个遍历结束语,其中传入的对象参数就是过滤的对象方法(Predicate<? super T> predicate),分别表示anyMatch(),遍历中其中有一个返回true,整个遍历结束,返回值是true,allMatch(),遍历中其中一个返回false,这个遍历结束,返回的值是false,noneMatch(),遍历中只要不返回true,遍历就不会结束,最后返回值是true
reduce()聚合操作,只需要是把集合汇集成一个对象,例如计算计算总金额
collect()lamdba表达式结束语,也就是集合的汇聚的使用方式,提供了极为丰富的API,可以实现任何需求,后面会详细描述

需要测试的,可以把下面的对象保存起来,后面的案例会用到这个类


/**
 * @author 游建诺
 * create by time 2019/6/17
 **/

public class OptionalValue<T> {

    private int i = -1;
    private T value;

    public OptionalValue() {

    }

    public OptionalValue(T t) {
        value = t;
    }

    public int setI(int i) {
        this.i = i;
        return i;
    }

    public int add(int i) {
        this.i += i;
        return this.i;
    }

    public int add() {
        return add(1);
    }

    public int getI() {
        return i;
    }

    public T setValue(T value) {
        this.value = value;
        return value;
    }

    public T getValue() {
        return value;
    }
}

案例1:创建一个0到10的字符串集合,并且把里面的内容打印出来

List<String> list =
        //开启一个遍历,并且从0开始递增
        Stream.generate(new OptionalValue<>()::add).
                //限制遍历10次
                limit(10).
                //把integer类型转换成String类型
                map(String::valueOf).
                //全部存入到list集合里面
                collect(java.util.stream.Collectors.toList());
//开启一个遍历,然后打印出里面
list.forEach(System.out::println);

在这里插入图片描述

案例2 在上述案例,从大到小打印出来

List<String> list =
        //开启一个遍历,并且从0开始递增
        Stream.generate(new OptionalValue<>()::add).
                //限制遍历10次
                limit(10).
                //加上排序
                sorted(Comparator.reverseOrder()).
                //把integer类型转换成String类型
                map(String::valueOf).
                //全部存入到list集合里面
                collect(java.util.stream.Collectors.toList());
//开启一个遍历,然后打印出里面
list.forEach(System.out::println);

在这里插入图片描述
案例3.在上述案例,只打印出偶数出来

List<String> list =
        //开启一个遍历,并且从0开始递增
        Stream.generate(new OptionalValue<>()::add).
                //限制遍历10次
                limit(10).
                //加上排序
                sorted(Comparator.reverseOrder()).
                //添加过滤,把为偶数的数值通过,奇数过滤掉
                filter(s -> s%2 == 0).
                //把integer类型转换成String类型
                map(String::valueOf).
                //全部存入到list集合里面
                collect(java.util.stream.Collectors.toList());
//开启一个遍历,然后打印出里面
list.forEach(System.out::println);

在这里插入图片描述
案例4 有一个字符串数组,把相同的过滤掉,并且返回一个list集合

String[] arrays = new String[]{"1", "1", "2", "3", "4", "1", "4"};
List<String> list = Stream.of(arrays).distinct()
        .collect(toList());
System.out.println(list);

在这里插入图片描述
案例5 给定一个集合,把第3位开始打印出后2为数值

String[] arrays = new String[]{"1", "1", "2", "3", "4", "1", "4"};
//从第array[3]开始打印后面两位数值
List<String> list = Stream.of(arrays).skip(3).limit(2)
        .collect(toList());
System.out.println(list);

在这里插入图片描述
案例6,给定一个集合,对其进行分页,每页的数量为3

//每一页分的次数
int pageSize = 3;
//创建一个0到20的集合
List<String> list = Stream.generate(new OptionalValue<>()::add).limit(20).map(String::valueOf).collect(toList());
//计算需要分多少页
int n = (list.size() / pageSize) + list.size() % pageSize == 0 ? 0 : 1;
List<List<String>> lists =
        //开启一个遍历
        Stream.generate(new OptionalValue<>()::add)
                //限制遍历次数
                .limit(n)
                //开始去截取字符串数组,这个是最优的写法,耗时最短
                //.map(i -> list.subList(i * pageSize, Math.min(list.size() ,i * pageSize + pageSize)))
                //采用lamdba表达式的写法
                .map(i -> list.stream().skip(i * pageSize).limit(pageSize).collect(toList()))
                .collect(toList());
System.out.println(lists);

在这里插入图片描述
为了方便对任何对象集合分页,我们可以封装成一个通用方法

public static <T> Stream<List<T>> pageStream(List<T> list, int limit) {
	int totalPage = (list.size() / limit) + (list.size() % limit == 0 ? 0 : 1);
	OptionalValue<Integer> nextOptionValue = new OptionalValue<>();
	return Stream.generate(nextOptionValue::add)
	        .limit(totalPage)
	        .map(i -> list.stream().skip(i * limit).limit(limit))
	        .map(stream -> stream.collect(toList()));
}

案例7:定义一个BigDecimal集合,计算里面的总数

//初始化一个BigDecimal
List<BigDecimal> list =
        Stream.generate(new OptionalValue<>()::add).limit(20).map(BigDecimal::new).collect(toList());
BigDecimal total = list.stream()
        //默认值是BigDecimal.ZERO,BigDecimal::add:表示加起来
        .reduce(BigDecimal.ZERO,BigDecimal::add);
System.out.println(total);

在这里插入图片描述
案例8:给定一个字符串集合判断里面是否存在一个字符串3

//初始化一个集合
List<String> list = Stream.of("1","2","4","5").collect(toList());
//判断是否存在集合里面存在3字符串
boolean is = list.stream().anyMatch(s -> "3".equals(s));
System.out.println("集合是否存在3的字符串:"+is);

在这里插入图片描述
上述案例只是例举了基础的用法,现在需要来介绍一些高级用法,主要是collect()方法提供的,这个只要是做流的聚合运算,其中有一个重要管理类Collectors,Collectors里面有很多静态方法,可以使用各种需求,这里面着重介绍一下Collector.of(),并且利用这个如何去定义自己的方法,因为这个方法是最最基本的,所有Collector里面的静态方法都是调用这个方法实现的
先定义一个对象User

public static class User{
        public String name;
        private int year;
        private String height;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getYear() {
            return year;
        }

        public void setYear(int year) {
            this.year = year;
        }

        public String getHeight() {
            return height;
        }

        public void setHeight(String height) {
            this.height = height;
        }
    }

案例1,给定一个user集合,把起转入一个HashMap里面去,并且HashMap对应的key是name

User user1 = new User("张三", 20, "170");
User user2 = new User("李四", 21, "171");
User user3 = new User("王二", 22, "172");
User user4 = new User("二狗", 23, "173");
List<User> users = Stream.of(user1, user2, user3, user4).collect(toList());
Map<String, User> list = users.stream().collect(Collectors.toMap(User::getName, user -> user));
System.out.println(list);
{李四=D.User(name=李四, year=21, height=171), 二狗=D.User(name=二狗, year=23, height=173), 张三=D.User(name=张三, year=20, height=170), 王二=D.User(name=王二, year=22, height=172)}

根据案例一,我们可以采用Collector.of()方法重新实现一遍,

User user1 = new User("张三", 20, "170");
User user2 = new User("李四", 21, "171");
User user3 = new User("王二", 22, "172");
User user4 = new User("二狗", 23, "173");
List<User> users = Stream.of(user1, user2, user3, user4).collect(toList());
//初始化一个HashMap
Supplier<Map<String, User>> supplier = HashMap::new;
//把user对象放入到map里面去
BiConsumer<Map<String, User>, User> accumulator = (map, user) -> map.putIfAbsent(user.getName(), user);
//多线程的时候需要使用这个方法,但是单线程无需关注
BinaryOperator<Map<String, User>> binaryOperator = (map1, map2) -> map1;
Map<String, User> list = users.stream().collect(Collector.of(supplier, accumulator, binaryOperator, IDENTITY_FINISH));
System.out.println(list);

上述案例还可以在进一步的设计,目前是把User按照里面的name为key存入到Map里面,但是如果name存在相同的话,需要把他们都保留下载,所以需要使用Map<String,List< User >>来存储,我们分别使用系统自带的方法和自定义实现的方法
系统自带的

User user1 = new User("张三", 20, "170");
User user2 = new User("张三", 21, "171");
User user3 = new User("王二", 22, "172");
User user4 = new User("二狗", 23, "173");
List<User> users = Stream.of(user1, user2, user3, user4).collect(toList());
Map<String, List<User>> list = users.stream().collect(Collectors.groupingBy(User::getName,toList()));
System.out.println(list);

结果:

{二狗=[D.User(name=二狗, year=23, height=173)], 张三=[D.User(name=张三, year=20, height=170), D.User(name=张三, year=21, height=171)], 王二=[D.User(name=王二, year=22, height=172)]}

定义实现方式

User user1 = new User("张三", 20, "170");
User user2 = new User("张三", 21, "171");
User user3 = new User("王二", 22, "172");
User user4 = new User("二狗", 23, "173");
List<User> users = Stream.of(user1, user2, user3, user4).collect(toList());
//初始化一个HashMap
Supplier<Map<String, List<User>>> supplier = HashMap::new;
//把user对象放入到map里面去
BiConsumer<Map<String, List<User>>, User> accumulator = (map, user) ->
        map.computeIfAbsent(user.getName(), name->new ArrayList<>()).add(user);
//多线程的时候需要使用这个方法,但是单线程无需关注
BinaryOperator<Map<String, List<User>>> binaryOperator = (map1, map2) -> map1;
Map<String, List<User>> list = users.stream().collect(Collector.of(supplier, accumulator, binaryOperator, IDENTITY_FINISH));
System.out.println(list);

上述自定义的的方法和相关的类绑定很深,可以自行封装一个泛型的方法,体验一下自己封装的快乐

案例2 采用Collector.of自带的方法,封装一个通过的方法,一方便大家学习参考
现有一个List<List<String> list 将其转换为List<String>,直接上代码了,不多解释

public static <T> Collector<List<T>, List<T>, List<T>> toLinearList() {
        Supplier<List<T>> supplier = ArrayList::new;
        BiConsumer<List<T>, List<T>> biConsumer =
                List::addAll;
        BinaryOperator<List<T>> binaryOperator = (emailMainEntities, emailMainEntities2) -> emailMainEntities2;
        return Collector.of(supplier, biConsumer, binaryOperator, Collector.Characteristics.IDENTITY_FINISH);
    }

如下使用

 List<List<String>> lists = Stream.generate(new OptionalValue<>()::add)
                .limit(10).map(i -> Stream.generate(new OptionalValue<>()::add).limit(2).map(String::valueOf).collect(toList()))
                .collect(toList());
List<String> list = lists.stream().collect(toLinearList());
System.out.println(list);

思考:如果使用系统自带的方法来实现这一的逻辑,联系方式:382034324@qq.com

案例3,现有一个List<String>集合,讲起拼接起来,采用逗号分开,并且字符串前有有中括号包裹起来

List<String> list = Stream.generate(new OptionalValue<>()::add).limit(10).map(String::valueOf).collect(toList());
String s = list.stream().collect(Collectors.joining(",", "[", "]"));
System.out.println(s);

在这里插入图片描述

后言

java8 lamdba已经基本上介绍完毕,在开发中尽量使用lamdba来使用,这样才会更容易写出更加优美的代码出来、lamdba表达式还有很多高深的写法值得大家去探索
谢谢大家!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值