java8中的lambda表达式和stream流

本文深入探讨Java中Lambda表达式和Stream流的应用,包括Lambda如何替代匿名内部类,使代码更简洁;Stream流如何简化集合操作,实现高效的数据处理。通过实例展示了不同类型的Lambda表达式,以及Stream流的常见操作,如过滤、映射、排序、收集等。

1.lambda表达式

lambda表达式是为了简化编程而生。在java中,一些很常见的操作总是包含一些“静态”代码,这里的静态值得是:无论我们在该操作中实现什么逻辑,这些代码必须得有而且形式也一样。比如使用匿名内部类的java线程,按钮的点击事件等。

lambda表达式就是要替代只有一个抽象方法的匿名内部类,让代码变得更简洁。

因为这些“静态”代码形式是固定的,所以编译器能够自动推断出来,或者可以理解为,那些被lambda表达式替换掉的“静态”代码编译器在编译时会自动补上(纯属个人理解)。

@FunctionalInterface 注解用于只有一个抽象方法的接口,而lambda表达式就是作用在这种表达式上的。

下面的代码根据抽象方法的类型分成四种进行讨论,分别是:无参无返回值,有参无返回值,无参有返回值,有参有返回值

定义 FunctionalInterface类型的接口

/**
 * FunctionalInterface注解只用于只有一个抽象方法的接口,有且只有这种接口可以用于lambda表达式
 */

/**
 * 抽象方法无参无返回值
 */
@FunctionalInterface
interface TestInterface1{
    void mmethod1();
}

/**
 * 抽象方法有参无返回值
 */
@FunctionalInterface
interface TestInterface2{
    void method2(String str1,String str2);
}

/**
 * 抽象方法无参有返回值
 */
@FunctionalInterface
interface TestInterface3{
    String method3();
}


/**
 * 抽象方法有参有返回值
 */
@FunctionalInterface
interface TestInterface4{
    boolean method4(String str1,String str2);
}

lambda表达式和等效的匿名内部类

/**
 * Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。
 */
public class TestClass {

    void testMethod1(TestInterface1 interface1){
        interface1.mmethod1();
    }

    void testMethod2(TestInterface2 interface2){
        interface2.method2("Hello","World");
    }

    void testMethod3(TestInterface3 interface3){
        String s=interface3.method3();
        System.out.println(s);
    }


    void testMethod4(TestInterface4 interface4){
        boolean b=interface4.method4("Hello","World");
        System.out.println(b);
    }

    /**
     * ***************************************************
     * 测试方法
     * ***************************************************
     */
    public void test(){
        /**
         * 无参无返回值
         */
        testMethod1(new TestInterface1() {
            @Override
            public void mmethod1() {
                System.out.println("匿名内部类无参无返回值");
            }
        });

        testMethod1(()->System.out.println("lambda无参无返回值"));


        /**
         * 有参无返回值
         */
        testMethod2(new TestInterface2() {
            @Override
            public void method2(String str1, String str2) {
                System.out.println("匿名内部类"+str1+str2);
            }
        });

        testMethod2((str1,str2)->{System.out.println("lambda有参无返回值"+str1+str2);});

        /**
         * 无参有返回值
         */
        testMethod3(new TestInterface3() {
            @Override
            public String method3() {
                String str1="Hello",str2="World";
                System.out.println("匿名内部类无参有返回值");
                return str1+str2;
            }
        });

        //两种写法
        testMethod3(()->  "Hello World");
        testMethod3(()->{
            String str1="Hello",str2="World";
            System.out.println("lambda无参有返回值");
            return str1+str2;

        });

        /**
         * 有参有返回值
         */
        testMethod4(new TestInterface4() {
            @Override
            public boolean method4(String str1, String str2) {
                System.out.println("匿名内部类有参有返回值");
                return str1.equals(str2);
            }
        });

        testMethod4((str1,str2)->{
            System.out.println("lambda表达式有参有返回值");
            return str1.equals(str2);

        });

        /**
         * java线程的lambda表达式写法
         */
        new Thread(()->{
            try{
                Thread.sleep(1000);
                System.out.println("java线程");
            }
            catch (Exception e){}
        }).start();

    }
}

参考博客:https://www.cnblogs.com/yulinfeng/p/8452379.html 感谢作者

2.Stream流

java8的stream流是为了更简洁的迭代处理集合。

下面的代码是stream常见的操作:

1.首先是两个构建两个用于测试的集合

    public List<Teacher> getTeachers(){
        List<Teacher> teachers=new LinkedList<>();
        teachers.add(new Teacher(19,"zhangSan","女"));
        teachers.add(new Teacher(45,"liSi","男"));
        teachers.add(new Teacher(20,"wangWU","女"));
        teachers.add(new Teacher(19,"找Liu","男"));
        return  teachers;
    }

    public List<Student> getStudents(){
        List<Student> students=new LinkedList<>();
        students.add(new Student(12,"xiaoMing"));
        students.add(new Student(10,"xiaoHong"));
        students.add(new Student(6,"xiaoHua"));
        return students;
    }

Teacher的构造参数依次为age,name,sex。Student的构造参数依次为age,name。

2.对集合最常见的一些操作:

    /**
     * 像filter,map 这样只描述 Stream,最终不产生新集合的方法叫作惰性求值方法;而像 count,collect 这样
     * 最终会从 Stream 产生值的方法叫作及早求值方法
     */

    /**
     * 判断一个操作是惰性求值还是及早求值很简单:只需看它的返回值。如果返回值是 Stream,
     * 那么是惰性求值;如果返回值是另一个值或为空,那么就是及早求值
     */
    /**
     * 方法引用是一种引用方法的轻量级语法,形如:ClassName::methodName。
     */
    public  void test(){
        /**
         * 统计年龄大于10的人数,filter是对stream对象里面的值进行过滤,该函数必须返回true或者false
         */
        long count=getTeachers().stream().filter(teacher -> teacher.getAge()>10).count();
        //lambda表达式后面也可以是一个代码块,类似于filter这种操作,需要在代码块中返回一个布尔值
        long count2=getTeachers().stream().filter(teacher -> {
            teacher.setAge(teacher.getAge()-5);
            return teacher.getAge()>10;}).count();

        //得到性别为女的集合并使用forEach做遍历输出
        List<Teacher> list=getTeachers().stream().filter(teacher -> teacher.getSex().equals("女"))
                .collect(Collectors.toList());
        //forEach,用于遍历集合,下面两行代码是等效的
        list.stream().forEach(teacher -> System.out.println(teacher));
        list.forEach(System.out::println);//传递的是方法的引用,编译环境能够知道需要将什么值作为参数传入到该方法中

        /**
         * map可以将一种类型转化为另一种类型,将一个流中的值转换成一个新的流,下面的代码是将Teacher转换成了Integer
         * map返回的是什么类型,就转换成了什么类型
         */
        List<Integer> list1=getTeachers().stream().map(teacher -> teacher.getAge()+12).collect(Collectors.toList());//返回年龄集合,每个年龄加了12岁
        List<Integer> list3=Stream.of("Hello","World","java","I").map(s -> s.length()).collect(Collectors.toList());//将字符串的长度组合成一个数组

        //使用Stream.of(...)的方式构建一个stream
        List<String> strings= Stream.of("a","b","c").collect(Collectors.toList());
        //转换为set集合
        Set<Integer> integers= Stream.of(23,34,11,66,12).collect(Collectors.toSet());

        /**
         * flatMap 方法可用 Stream 替换值,然后将多个 Stream 连接成一个 Stream
         * flatMap将多个类型的集合,合并成一个集合,但是必须返回Stream类型。
         * Teacher和Student都是Object子类,所以合并后用Object接收
         */
        /**
         * 控制台输出
         * Teacher{age=19, name='zhangSan', sex='女', students=null}
         * Teacher{age=45, name='liSi', sex='男', students=null}
         * Teacher{age=20, name='wangWU', sex='女', students=null}
         * Teacher{age=19, name='找Liu', sex='男', students=null}
         * Student{age=12, name='xiaoMing'}
         * Student{age=10, name='xiaoHong'}
         * Student{age=6, name='xiaoHua'}
         */
        List<Object> list4=Stream.of(getTeachers(),getStudents()).flatMap(objects -> objects.stream()).collect(Collectors.toList());
        System.out.println(list4);

        /**
         * 获取集合中的最大值和最小值
         * 使用get获取操作后的对象或者值
         */
        Student stuAgeMin=getStudents().stream().min(Comparator.comparing(student -> student.getAge())).get();//获取年龄最小的学生
        Student stuAgeMax=getStudents().stream().max(Comparator.comparing(student -> student.getAge())).get();//获取年龄最大的学生

        /**
         * 排序
         */
       List<Teacher> teacherList=getTeachers().stream().sorted(Comparator.comparing(teacher -> teacher.getAge())).collect(Collectors.toList());
        System.out.println(teacherList);//按老师年龄输出,年龄由小到大

       List<Integer> numbers=Stream.of(9,12,5,2,11).sorted().collect(Collectors.toList());
       numbers.forEach(System.err::print);//输出:2 5 9 11 12
        System.out.println(stuAgeMin);
    }

3.收集器collect更多的用法:

    public void collectionTest(){
        /**
         * 生成一个字符串,该集合最后生成一个具有一定形式的字符串
         * 希望将一个集合转换成这种形式的字符串: {item1.item2,item3...}
         */

        //joining(...),第一个参数表示分隔符,第二个参数表示前缀,第三个参数表示后缀
        String str=Stream.of(12,33,43,67,77,88,93).map(s->s.toString()).collect(Collectors.joining(",","{","}"));
        System.err.println(str);//输出:{12,33,43,67,77,88,93}
        /**
         * 转换成其他的集合类
         *
         * 之前的操作都是将集合转换为List,Set这些抽象接口,Stream类在背后自动为我们选择合适的类型,
         * 我们可以自定义转换的集合类型
         */
        List<Integer> numbers2=Stream.of(9,12,5,2,11).sorted().collect(Collectors.toCollection(LinkedList::new));
        List<String> numbers3=Stream.of(9,12,5,2,11).map(a -> a+"").collect(Collectors.toCollection(Vector::new));
        //转换为Map对象,key和value分别是老师的名字和性别

        Map<String,String> stringMap=getTeachers().stream().collect(Collectors.toMap(teacher->teacher.getName(),teacher -> teacher.getSex()));
        for (Map.Entry<String,String> stringListEntry : stringMap.entrySet()) {
            System.err.println(stringListEntry.getKey()+":"+stringListEntry.getValue());
        }

        /**
         * 转换成值
         */
       Optional<Teacher>t=getTeachers().stream().collect(Collectors.maxBy(Comparator.comparing(teacher -> teacher.getAge())));
       Optional<Teacher>t2=getTeachers().stream().collect(Collectors.minBy(Comparator.comparing(teacher -> teacher.getAge())));
       //计算平均年龄(平均数有可能是小数,所以要用都double来接收)
       double averagingAge= getTeachers().stream().collect(Collectors.averagingInt(teacher->teacher.getAge()));
       System.out.println(averagingAge);

        /**
         * 数据分块
         *
         * 该流操作是将一个集合其分解成两个集合,比如一个Teacher集合,想根据性别分成两个集合,这时就可以使用数据分块了
         *
         * 它使用 Predicate 对象判断一个元素应该属于哪个部分,并根据布尔值返回一
         * 个 Map 到列表。因此,对于 true List 中的元素,Predicate 返回 true;对其他 List 中的
         * 元素,Predicate 返回 false。
         */
        Map<Boolean,List<Teacher>> maps=getTeachers().stream().collect(Collectors.partitioningBy(teacher -> teacher.getSex().equals("女")));
        List<Teacher> t1=maps.get(true);   t1.stream().forEach(teacher -> System.out.println(teacher));//输出所有女老师
        maps.get(false).stream().forEach(teacher -> System.out.println(teacher));//输出所有男老师

        /**
         * 数据分组
         *
         * 数据分组是一种更自然的分割数据操作,与将数据分成 ture 和 false 两部分不同,可以使
         * 用任意值对数据分组,返回的Map对象key是分组的字段
         * 和sql中的group by很像
         */
        Map<String,List<Teacher>>teacherListMap=getTeachers().stream().collect(Collectors.groupingBy(teacher -> teacher.getSex()));
        List<Teacher> t3=teacherListMap.get("女");   t3.stream().forEach(teacher -> System.out.println(teacher));//输出所有女老师
        teacherListMap.get("男").stream().forEach(teacher -> System.out.println(teacher)); //输出所有男老师

        /**
         * 组合收集器
         */
        //根据老师性别分组并且输出个数(非常类似sql中的group by和聚合函数的使用),使用了counting()
        Map<String,Long> counts=getTeachers().stream().collect(Collectors.groupingBy(teacher -> teacher.getSex(),Collectors.counting()));
        System.err.println("女老师个数为"+counts.get("女"));//输出:女老师个数为2
        System.err.println("男老师个数为"+counts.get("男"));//输出:男老师个数为4

        //按照老师的性别输出老师的姓名,使用了mapping(...)方法,第一参数表示分组之后需要输出什么值,这里是输出name,第二个是以
        //什么集合形式输出,这里是以List集合的方式输出
        Map<String,List<String>> stringListMap=getTeachers().stream().collect(Collectors.groupingBy(teacher -> teacher.getSex(),Collectors.
                mapping(teacher -> teacher.getName(),Collectors.toList())));
        stringListMap.get("男").stream().forEach(System.err::println);//输出所有男老师的姓名
        stringListMap.get("女").stream().forEach(System.err::println);//输出所有女老师的姓名

    }

4.常见的终止操作

allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值