Lambda表达式 方法引用 StreamAPI(全到不能再全了)

 

前言

Lambda表达式于Java1.8更新引入 极大的简化了对函数式编程的复杂度


目录

一、Lambda表达式

1.Lambda表达式的适用场景

2.Lambda表达式的特点

3.基本格式

4.一些官方定义的函数式接口

 5.官方函数式接口用法与自定义函数式接口

        5.1Supplier的用法

        5.2Consumer的用法

         5.3BiConsumer的用法

        5.4Function的用法

         5.5UnaryOperator的用法

         5.6BiFunction的用法

         5.7BiUnaryOperator的用法

         5.8自定义函数式接口

二、方法引用

1.什么是方法引用

2.方法引用的分类与规范格式

        2.1规范格式

        2.2静态方法引用

        2.3实例方法引用

         2.4对象方法引用

        2.5构造方法引用

 三、StreamApi

1.Stream的特性

2.Stream运行机制

3.Stream的创建

        1.通过数组

        2.通过集合

         3.通过Stream.generate方法

         4.通过Stream.inerate方法

        5.通过其他API

4.Stream常用API

5.具体用法

四、一段很有趣的代码

总结


一、Lambda表达式

Lambda表达式允许Java将函数作为一个参数传入 让你从此告别匿名内部类

举个简单的例子:按字符串长度来排序输出

 List<String> list = Arrays.asList("java","python","scala","c++");
        //普通匿名内部类方式 重写比较器 又臭又长
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length()-o2.length();
                }
        });
        System.out.println(list.toString());
        //lambda表达式 两行搞定
        Collections.sort(list,(a,b)->a.length()-b.length());
        list.forEach(System.out::println);

Lambda表达式就是如此简单 心动了吗?

1.Lambda表达式的适用场景

Lambda表达式适用于任何有函数式接口的地方 函数式接口即为只有一个抽象方法的接口(Object类中的方法除外)

2.Lambda表达式的特点

1.函数式编程 继面向接口编程之后另一大思想:面向函数编程

2.参数类型自动推断 传进去就可以 完全“散养”

3.代码量少 简洁 看起来很牛逼(笑)

3.基本格式

Lambda表达式根据不同的情况有很多很多种写法 如下所示

()->{};
()->{System.out.println(1);};
()-> System.out.println(1);
()->{return 100};
()->100;
()->null;
(x)->x+1;
x->x+1;
(int x)->{return x+1};

如果后续只有一个语句 {}可以省略不写 ()也可以省略不写 但是推荐不要吝啬那么两个括号

4.一些官方定义的函数式接口

Supplier代表一个输出
Consumer代表一个输入
BiConsumer代表两个输入
Function 代表一个输入一个输出(一般类型不同 也可相同)
UnaryOperator 代表一个输入一个输出(类型相同)
BiFunction代表两个输入一个输出(一般类型不同也可以相同)
BiUnaryOperator代表两个输入一个输出(类型相同)

 5.官方函数式接口用法与自定义函数式接口

        5.1Supplier的用法

Supplier<String> supplier = ()-> "aaa";
        System.out.println(supplier.get());

        5.2Consumer的用法

Consumer<String> consumer = (String string)-> System.out.println(string);
        consumer.accept("bbb");

         5.3BiConsumer的用法

BiConsumer<String,Integer> biConsumer = (str,num)-> System.out.println(str+"\n"+num);
        biConsumer.accept("abc",4);

        5.4Function的用法

Function<String,Integer> function = (String str)-> {return str.length();};
        System.out.println(function.apply("abcdefg"));

         5.5UnaryOperator的用法

UnaryOperator<String> unaryOperator = (str)-> {return str;};
        System.out.println(unaryOperator.apply("lwpaizw"));

         5.6BiFunction的用法

BiFunction<String,String,Integer> biFunction = (str,str1)->{return str.length()+str1.length();};
        System.out.println(biFunction.apply("bbb","ccc"));

         5.7BiUnaryOperator的用法

 BinaryOperator<String> binaryOperator = (str,str1)->{return str+str1;};
        System.out.println(binaryOperator.apply("alwb", "aizw"));

         5.8自定义函数式接口

//第一个函数式接口
@FunctionalInterface
public interface StudentDao {
    void get(Student student);
}

//第二个函数式接口
@FunctionalInterface
public interface TeacherDao {
    int insert(Teacher teacher);
}

//一些操作 Student Teacher类里面什么都没有就不放出来了
TeacherDao teacherDao = (teacher)->{return 1;};
TeacherDao teacherDao1 = (Teacher teacher)->{return 2;};
TeacherDao teacherDao2 = (teacher)->3;
System.out.println(teacherDao.insert(new Teacher()));
System.out.println(teacherDao1.insert(new Teacher()));
System.out.println(teacherDao2.insert(new Teacher()));

StudentDao studentDao = (student)-> System.out.println("zhangsan");
StudentDao studentDao1 = (student)-> System.out.println("插入学生"+student); studentDao1.get(new Student());
studentDao.get(new Student());


二、方法引用

1.什么是方法引用

方法引用是用来直接访问类或实例已经存在的方法或构造方法 提供了一种引用而不执行方法的方式

2.方法引用的分类与规范格式

        2.1规范格式

类型语法对应的Lambda表达式
静态方法引用类名::staticMethod(args)->类名.staticMethod(args)
实例方法引用inst::instMethod(args)->inst.instMethod(args)
对象方法引用类名::instMethod(args)->类名.instMethod(args)
构造方法引用类名::new (args)->new 类名(args)

        2.2静态方法引用

//如果函数式接口的实现可以通过调用另一个静态方法实现 就可以适用静态方法引用
public class Test1 {
    public static void main(String[] args) {
        Supplier<String> s1 = ()->Test1.put();
        Supplier<String> s2 = Test1::put;
        System.out.println(s1.get());
        System.out.println(s2.get());

        Consumer<String> c1 = (str)->Test1.put();
        Consumer<Integer> c2 = Test1::fun;
        c1.accept("abc");
        c2.accept(123);

        Function<String,String> f1 = (str)->Test1.toUpperCase(str);
        Function<String,String> f2 = Test1::toUpperCase;
        Function<String,String> f3 = (str)->{return Test1.toUpperCase(str);};
        Function<String,String> f4 = Play::too;
        System.out.println(f1.apply("abc"));
        System.out.println(f2.apply("abc"));
        System.out.println(f3.apply("abc"));
        System.out.println(f4.apply("abc"));
    }

    static String put(){
        System.out.println("put");
        return "put....";
    }

    static Integer fun(int i){
        System.out.println(i);
        return 1;
    }

    static String toUpperCase(String str){
        return str.toUpperCase();
    }
}

class Play{
    static String too(String string){
        return string.toUpperCase();
    }
}

        2.3实例方法引用

//如果函数式接口的实现可以通过调用另一个实例的方法实现 就可以使用实例方法引用
public class Test2 {
    public static void main(String[] args) {
        Supplier<String> s1 = ()->new Test2().fun();
        Supplier<String> s2 = new Test2()::fun;
        System.out.println(s1.get());
        System.out.println(s2.get());
        
        //每次都要new一个对象好麻烦
        Consumer<Integer> c1 = (size)->new Test2().getSize(size);
        Consumer<Integer> c2 = new Test2()::getSize;

        Test2 test = new Test2();   //所以我们唯一的创建一个Test2对象 就不用一直new了

        Consumer<Integer> c3 = (size)->test.getSize(size);
        Consumer<Integer> c4 = test::getSize;
        c1.accept(123);
        c2.accept(123);
        c3.accept(123);
        c4.accept(123);

        Function<String,String> f1 = (str)->test.toUpperCase(str);
        Function<String,String> f2 = test::toUpperCase;
        System.out.println(f1.apply("abc"));
        System.out.println(f2.apply("abc"));

        BiFunction<String,String,Integer> bf = (str,str2)->test.too(str,str2);
        BiFunction<String,String,Integer> bf2 = test::too;
        System.out.println(bf.apply("abc", "def"));
        System.out.println(bf2.apply("abc", "def"));
    }

    public String fun(){
        return "put...";
    }

    public void getSize(int size){
        System.out.println("size:"+size);
    }

    public void foo(String string){
        System.out.println("foo");
    }

    public String toUpperCase(String str){
        return str.toUpperCase();
    }

    public Integer too(String str,String str2){
        return str.length()+str2.length();
    }
}

         2.4对象方法引用

//其实我感觉 对象方法引用和实例方法引用区别不大...很少会用到
public class Test3 {
    public static void main(String[] args) {
        Consumer<Too> c1 = (too)->new Too().fun();
        c1.accept(new Too());
        Consumer<Too> c2 = (too)->new Too2().fun();
        c2.accept(new Too());
        Consumer<Too> c3 = Too::fun;
        c3.accept(new Too());
        Consumer<Too2> c4 = Too2::fun;
        c4.accept(new Too2());

        BiConsumer<Too2,String> bc1 = (too2,str)->new Too2().show(str);
        BiConsumer<Too2,String> bc2 = Too2::show;
        bc1.accept(new Too2(),"aaa");
        bc2.accept(new Too2(),"bbb");

        BiFunction<Too,String,Integer> bf1 = (too,str)->new Too().play(str);
        BiFunction<Too,String,Integer> bf2 = Too::play;
        bf1.apply(new Too(),"aaa");
        bf2.apply(new Too(),"bbb");

        BiFunction<Too,String,Integer> bf3 = (too,str)->new Too().foo(str);
        bf3.apply(new Too(),"abc");
        BiFunction<Too,String,Integer> bf4 = Too::foo;
        bf4.apply(new Too(),"def");
    }
}

class Too{
    public void fun(){
        System.out.println("funning....");
    }

    public Integer foo(String s){
        System.out.println("foo...");
        return 1;
    }

    public Integer play(String s){
        System.out.println("playing...." +s);
        return 1;
    }
}

class Too2{
    public void fun(){
        System.out.println("funning....2");
    }

    public Integer foo(String s){
        return 1;
    }

    public void show(String str){
        System.out.println("show Too2");
    }
}

        2.5构造方法引用

//如果函数式接口的实现可以通过调用另一个类的构造方法来实现 就可以使用构造方法引用
public class Test4 {
    public static void main(String[] args) {
        Supplier<Person> s1 = ()->new Person();
        s1.get();
        Supplier<Person> s2 = Person::new;
        s2.get();

        Supplier<List> s3 = ArrayList::new;
        Supplier<Set> s4 = HashSet::new;
        Supplier<Thread> s5 = Thread::new;
        s3.get();
        s4.get();
        s5.get();

        Consumer<String> c1 = (name)->new Account(name);
        Consumer<String> c2 = Account::new;
        Consumer<Integer> c3 = (age)->new Account(age);
        Consumer<Integer> c4 = Account::new;
        c1.accept("aaa");
        c2.accept("bbb");
        c3.accept(1);
        c4.accept(2);

        Function<String,Account> f1 = (str)->new Account(str);
        f1.apply("abc");
        Function<Integer,Account> f2 = Account::new;
        f2.apply(1);

    }
}

class Person{
    public Person(){
        System.out.println("调用无参构造方法");
    }
}

class Account{
    public Account(String name){
        System.out.println("调用name构造方法" + name);
    }

    public Account(int age){
        System.out.println("调用age构造方法" + age);
    }
}

 三、StreamApi

Stream是一组是用来处理数组与集合的API

1.Stream的特性

1.不是数据结构 没有内存存储

2.不支持索引访问

3.延迟计算

4.支持并行

5.很容易生成数组或集合(我好喜欢)

6.支持过滤、查找、转换、汇总、聚合等操作

2.Stream运行机制

Stream分为源source 中间操作 终止操作

流的源source可以是一个数组 一个集合 一个IO流等等

Stream只有遇到终止操作 才会开始执行遍历操作

3.Stream的创建

        1.通过数组

 //通过数组创建stream
    static void gen1(){
        String[] str = {"a","b","c","d"};
        Stream<String> strs = Stream.of(str);
        strs.forEach(System.out::println);
    }

        2.通过集合

//通过集合创建stream
    static void gen2(){
        List<String> list = Arrays.asList("1","2","3","4");
        Stream<String> list1 = list.stream();
        list1.forEach(System.out::println);
    }

         3.通过Stream.generate方法

/通过generate方法创建stream
    static void gen3(){
        Stream<Integer> generate = Stream.generate(() -> 1);
        generate.limit(10).forEach(System.out::println);
    }

         4.通过Stream.inerate方法

//通过iterator方法创建stream
    static void gen4(){
        Stream<Integer> iterate = Stream.iterate(1, x -> x + 1);
        iterate.limit(10).forEach(System.out::println);
    }

        5.通过其他API

 //通过其他方式创建stream
    static void gen5(){
        String str = "abcdefg";
        IntStream chars = str.chars();
        chars.forEach(System.out::println);
    }

4.Stream常用API

中间操作有

过滤filter
去重distinct
排序

sorted

截取limit、skip
转换map、flatMap
其他peek

终止操作有

循环

forEach

计算min、max、count、average
匹配anyMatch、allMatch、noneMatch、findAny、findFirst
汇聚reduce
收集器toArray collect

5.具体用法

 //找出全部的偶数 使用filter方法过滤
        Arrays.asList(1,2,3,4,5).stream().filter((x)->x%2==0).forEach(System.out::println);

        //求出结果集中所有偶数的总数 使用count方法统计
        long count = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9).stream().filter((x) -> x % 2 == 0).count();
        System.out.println(count);

        //求出结果集中所有偶数的和 使用IntStream中的sum方法计算
        int sum = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9).stream().filter((x) -> x % 2 == 0).mapToInt(x->x).sum();
        System.out.println(sum);

        //求集合中的最大值 使用max方法
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6);
        Optional<Integer> max = integers.stream().max((a, b) -> a - b);
        System.out.println(max);
        System.out.println(max.get());

        //求集合中的最小值 使用min方法 与最大值类似
        System.out.println(integers.stream().min((a, b) -> a - b).get());

        //求集合中符合条件的一个值 使用findany方法 findfirst方法与之类似
        Optional<Integer> any = integers.stream().filter((x) -> x % 2 == 0).findAny();
        System.out.println(any.get());

        //求集合中最大值与最小值 不使用max与min方法 使用sorted方法
        Optional<Integer> any1 = integers.stream().sorted().findAny();
        System.out.println(any1.get());
        Optional<Integer> any2 = integers.stream().sorted((a, b) -> b - a).findAny();
        System.out.println(any2.get());

        //排序字符串 使用sorted方法
        //按字典顺序排序
        Arrays.asList("java","c#","python","scala").stream().sorted().forEach(System.out::println);
        //按字符串长度排序
        Arrays.asList("java","c#","python","scala").stream().sorted((a,b)->a.length()-b.length()).forEach(System.out::println);

        //将集合中元素进行过滤 同时返回一个集合对象 使用collect方法
        List<Integer> collect = integers.stream().filter((x) -> x % 2 == 0).collect(Collectors.toList());
        collect.forEach(System.out::println);

        //去重 使用distince方法与使用collect方法
        List<Integer> integers1 = Arrays.asList(1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 7);
        Stream<Integer> distinct = integers1.stream().distinct();
        distinct.forEach(System.out::println);

        Arrays.asList(1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 7).stream().distinct().forEach(System.out::println);
        Arrays.asList(1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 7).stream().collect(Collectors.toSet()).forEach(System.out::println);

        //打印20-30的集合数据 使用iterate、limit、skip方法
        Stream.iterate(1,(x)->x+1).limit(50).skip(20).limit(10).forEach(System.out::println);

        //求字符串中数字的和 结合方法引用
        String str = "11,22,33,44,55";
        System.out.println(Stream.of(str.split(",")).mapToInt((x) -> Integer.valueOf(x)).sum());
        System.out.println(Stream.of(str.split(",")).map((x) -> Integer.valueOf(x)).mapToInt((x) -> x).sum());
        System.out.println(Stream.of(str.split(",")).mapToInt(Integer::valueOf).sum());
        System.out.println(Stream.of(str.split(",")).map(Integer::valueOf).mapToInt((x) -> x).sum());

        //创建自定义对象
        String string = "java,c++,python";
        Stream.of(string.split(",")).map((x)->new Person(x)).forEach(System.out::println);
        Stream.of(string.split(",")).map(Person::new).forEach(System.out::println);
        Stream.of(string.split(",")).map((x)->Person.build(x)).forEach(System.out::println);
        Stream.of(string.split(",")).map(Person::build).forEach(System.out::println);

        //求字符串中数字的和 并将每个数打印 使用peek方法
        System.out.println(Stream.of(str.split(",")).peek(System.out::println).mapToInt(Integer::valueOf).sum());

        //allMatch方法
        System.out.println(Arrays.asList(1, 2, 3, 4).stream().allMatch(x -> x > 0));

        //合并两个stream流 使用concat方法
        List<String> strings2 = Arrays.asList("a", "l", "w", "b", "a", "i");
        List<String> strings3 = Arrays.asList("z", "w");
        Stream stream1 = strings2.stream();
        Stream stream2 = strings3.stream();
        Stream stream3 = Stream.concat(stream1,stream2);
        stream3.forEach(System.out::println);
//        stream3.forEach((s)-> System.out.println(s));
//        System.out.println(stream3.collect(Collectors.toList()));

        //flatmaap方法
        List<Map<String,List<Student2>>> students = getStudent();
        List<Student2> boy = students.stream().flatMap((classMap) -> classMap.get("男生").stream()).collect(Collectors.toList());
        System.out.println(boy);

    }
    private static List<Map<String,List<Student2>>> getStudent(){
        Map<String,List<Student2>> classMap = new HashMap<>();
        List<Student2> boys = new ArrayList<>();
        List<Student2> girls = new ArrayList<>();
        boys.add(new Student2("zhangsan",14,"1-1","男"));
        boys.add(new Student2("lisi",14,"1-2","男"));
        boys.add(new Student2("wangwu",14,"1-3","男"));
        girls.add(new Student2("xiaohong",14,"1-4","女"));
        girls.add(new Student2("xiaomei",14,"1-5","女"));
        girls.add(new Student2("xiaolan",14,"1-6","女"));
        classMap.put("男生",boys);
        classMap.put("女生",girls);

        Map<String,List<Student2>> classMap2 = new HashMap<>();
        List<Student2> boys1 = new ArrayList<>();
        List<Student2> girls1 = new ArrayList<>();
        boys1.add(new Student2("zhangsan",14,"2-1","男"));
        boys1.add(new Student2("lisi",14,"2-2","男"));
        boys1.add(new Student2("wangwu",14,"2-3","男"));
        girls1.add(new Student2("xiaohong",14,"2-4","女"));
        girls1.add(new Student2("xiaomei",14,"2-5","女"));
        girls1.add(new Student2("xiaolan",14,"2-6","女"));
        classMap2.put("男生",boys1);
        classMap2.put("女生",girls1);

        List<Map<String,List<Student2>>> gradeList = new ArrayList<>();
        gradeList.add(classMap);
        gradeList.add(classMap2);
        return gradeList;
    }

四、一段很有趣的代码

 让我们看一看这段代码:

//一段有趣的代码
        Stream<Integer> integerStream = integers.stream().filter((x) -> {
            System.out.println("运行代码");
            return x % 2 == 0;
        });
        System.out.println(integerStream.findAny().get());

integers的值是1,2,3,4,5,6 

 你们觉得会输出几个“运行代码”呢?

答案是两个

不妨想想是为什么 试着修改一下integers的值再看看输出结果呢?

总结

以上就是Lambda表达式、方法引用、Stream的用法了 之所以学习Lambda表达式 一方面减少自己的工作量 一方面也要看懂这是什么 别人为什么这样写 还有就是 别人用匿名内部类写了很长很长 而我使用Lambda表达式写了一两行 我们实现了同样的需求 这样不就显得很牛吗(哈哈哈)

还记得我们提到过面向函数编程这一思想 Java1.8之所以费这么大力气来引入函数式编程 主要原因是

1.代码简洁 意图明确 使用stream让你告别for循环

2.Java函数式编程使编写并运行程序变得如此简单 真的很简单!

假设有一个需求(是什么无所谓了) Java可能需要三十行代码 而python等语言可能四五行就搞定了 我觉得这可能也是引入函数式编程的一个原因吧

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我不是朋也

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值