基础——lambda表达式

lambda表达式

在这里插入图片描述
说到了Lambda就不得不说函数式编程。在数学中函数f(x)司空见惯,函数要求有输入量也有输出量并且还有表达式,简单地说就是“拿什么东西做什么事情最后输出我们想要的结果”。然而,在java中面向对象编程强调“必须通过对象的形式来做事情”,如此函数式编程与面向对象编程便区别了出来,前者强调“做什么”,后者强调“以什么形式做”。就好像有人说去日本,函数式编程就说一句去日本,而面向对象编程便要考虑是坐飞机去日本还是开船去日本。

lanmbda表达式就是函数式编程的一种体现,它强调的是做什么,而不讲究以什么形式去做。使得编程的关注点聚焦于方法。

lambda表达式的形式

在Java8以前的java代码中,经常使用内部匿名类的形式作为接口实现类对象参数

	new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("任务B执行了...");
            }
        }).start();

这样的代码看起来很繁琐,如果使用了Lambda后

	new Thread(
           () -> {
                System.out.println("任务C执行了...");
            }
        ).start();

从代码结构中就可以看出规律,lanmbda表达式的书写格式就是:
只需要按照匿名内部类的形式写出代码,然后保留抽象方法的()和{},在()和{}之间添加一个->
因此lambda表达式的组成就是:() -> {},其中()中类型可以省略,要是只有一个参数()也可以省略;{}中如果只有一行语句,那么return和末尾分号都可以省略;->是绝对不能省略的。

lambda表达式的使用前提

  1. 使用lambda代替的是函数式接口抽象方法的实现,也就是要求接口中有且仅有一个抽象方法。
  2. 必须要有方法使用接口作为参数。也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

Java 8中专门为函数式接口引入了一个新的注解: @FunctionalInterface ,专门用来检测是不是函数式接口

(重点!)常用函数式接口

1、Comparator:比较型接口,用于两个对象的比较,其中抽象方法compare接受两个参数o1,o2,如果return o1 - o2表示升序,如果return o2 - o1表示降序(o1和o2都不是引用类型对象)

 		Person[] arr = new Person[]{
            new Person("ttt",12),
                new Person("qw",24),
                new Person("rr",38),
                new Person("cc",45),
                new Person("uu",38),
        };

		// 匿名类写法
        Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        });
		
		// lambda标准写法
        Arrays.sort(arr, (Person o1, Person o2) -> {
                return o2.getAge() - o1.getAge();
            });
       
       // lambda简化写法
       Arrays.sort(arr,(o1, o2) -> o1.getAge() - o2.getAge());

2、Conusmer:消费型接口,给他的抽象方法accept一个参数,就叫消费一个对象。

	public static void main(String[] args) {
        String str = "Hello World";
        // 给一个str对象消费掉
        // 匿名内部类的形式
        method(str, new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        // lanmbda标准形式
        method(str,(String s)->{
            System.out.println("长度:" + s.length());
        });
        // lambda简化形式
        method(str,s -> System.out.println("全部大写" + s.toUpperCase()));
    }

    public static void method(String str,Consumer<String> consumer) {
        // 调用抽象方法
        consumer.accept(str);
    }

3、Predicate:判断型接口,返回一个boolean类型作为判断结果。

	public static void main(String[] args) {
        String str = "Helloworld";
        // 匿名内部类对象的方式
        method(str, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                //判断字符串长度是否大于5
                return s.length()>5;
            }
        });
        // lambda表达式的标准方式
        method(str,(String s)->{
            return s.length()>5;
        });
        // lambda表达式的简化形式
        method(str,s->s.length()>5);
    }
    /**
     * 定义方法,使用函数式接口Predicate作为参数
     */
    public static void method(String str,Predicate<String> p){
        boolean result = p.test(str);
        System.out.println(result);
    }

Stream流

Java8以后,引入了Stream,此流非彼流,它本质上强调的是工作流程,用于解决集合类中既有的弊端,比如在遍历集合的同时要做一些筛选过滤。
在这里插入图片描述

一、展示什么叫优雅

Stream综合练习

  1. 第一个队伍只要名字为3个字的成员姓名;
  2. 第一个队伍筛选之后只要前3个人;
  3. 第二个队伍只要姓张的成员姓名;
  4. 第二个队伍筛选之后不要前2个人;
  5. 将两个队伍合并为一个队伍;
  6. 打印整个队伍的姓名信息。
 public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("老子");
        one.add("庄子");
        one.add("孙子");
        one.add("洪七公");



        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");

        Stream.concat(one.stream().filter(s -> s.length() == 3).limit(3)
                ,two.stream().filter(s -> s.startsWith("张")).skip(2))
                .forEach(s-> System.out.println(s));

    }

三行代码完成上面的需求!过程清晰明了!

二、获取Stream对象

1、通过Collection 集合

public default Stream<E> stream​() 方法获取,因此Collection的实现类都能获取流对象。

  • 注意:Map集合的实现类要通过Stream方式操作的话只能先获取key集合、value集合或者entry集合,通过Set集合来进行操作。

2、Stream接口的静态方法

static <T> Stream<T> of​(T… values) ,可以传入数组来获取流对象。

三、Stream常用方法

1、filter方法

Stream <T> filter(Predicate predicate) : 按照方法参数predicate对Stream流对象中的元素进行过滤,并返回新的Stream流对象

2、limit方法和skip方法

Stream<T> limit(long n): 获取流对象中的前n个元素,返回新的Stream流对象

Stream<T> skip(long n): 跳过流对象中的前n个元素,返回新的Stream流对象

3、contact方法

static <T> Stream<T> concat(Stream<T> a, Stream<T> b): ,把两个流对象a和b合并成一个流对象并返回新的流对象

4、count方法

long count() : 终结方法,调用此方法后,Stream流对象将不可以继续使用

5、forEach方法

void forEach(Consumer<T> action): 终结方法,调用此方法后,Stream流对象将不可以继续使用
该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理,不保证顺序.

使用过程可能抛出的异常

Exception in thread “main” java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.(AbstractPipeline.java:203)
at java.util.stream.ReferencePipeline.(ReferencePipeline.java:94)
at java.util.stream.ReferencePipeline$StatelessOp.(ReferencePipeline.java:618)
at java.util.stream.ReferencePipeline$2.(ReferencePipeline.java:163)
at java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:162)
at com.lambda.DemoFilter.main(DemoFilter.java:16)

这是重复使用了已经用过的Stream的对象而导致的问题,因为每个Stream的对象只能用一次。

总结:

1、终结方法:count、forEach,返回值都不是Stream对象,不能再链式调用
2、流对象只能被使用一次

总结

Lambda表达式简化了Java编程的复杂性,为函数式接口编程提供了一个面向函数编程的方式,不用再书写繁琐的匿名内部类。

猜你喜欢

转载自blog.csdn.net/weixin_38708854/article/details/106531544