Lambda表达式常见用法

Lambda介绍

Lambda,别名函数式编程

函数式编程是一种编程范式。它把计算当成是数学函数的求值,从而避免改变状态和使用可变数据。它是一种声明式的编程范式,通过表达式和声明而不是语句来编程。

Lambda表达式的优缺点

优点:

  1. 代码简洁,开发迅速,方便函数式编程
  2. 非常容易进行并行计算,尤其适用于遍历结果,循环计算数值或者赋值时
  3. 结合 hashmap 的 computeIfAbsent 方法,递归运算非常快。java有针对递归的专门优化。
  4. 减少匿名内部类的创建,节省资源(匿名内部类会产生一个class文件,而Lambda不会产生class文件,所以在类加载时,Lambda表达式的效率更高。)

缺点:

  1. 代码可读性变差,不容易进行调试
  2. 不易于后期维护,必须熟悉lambda表达式和抽象函数中参数的类型
  3. 不能再foreach中修改forEach外面的值,强制类型转换不方便(一定要搞清楚到底是 map 还是 mapToDouble 还是 mapToInt)
  4. 性能方面,如果不并行计算,很多计算未必有传统的for性能要高(并行计算有时需要预热才显示出效率优势)

使用场景:

  1. 集合操作:Lambda表达式可以方便地对集合进行筛选、转换和聚合等操作。
  2. 接口的实现:当需要实现一个只有一个抽象方法的接口时,可以使用lambda表达式代替匿名内部类。
  3. 并行处理:使用Stream API和lambda表达式可以方便地进行并行处理,提高性能。

符号表示

lambda是一个匿名函数

() 里的表示参数
// 括号中的参数只有一个,()可以省略
// 括号中的参数列表的数据类型,可以省略不写(编译器自己会根据上下文语境推断出类型的)

{} 里的表示方法体
// Lambda体只有一句语句({ 只有一句语句 }),无论是否有返回值,都可以省略({ }, return , 分号 )
// 注意:在省略{ }的同时要(return)和分号一起省略

-> 表示lambda运算符

并行处理

什么是并行流?

就是利用多核 CPU 的计算能力来加速流式处理。当一个流进行并行处理时,数据会被分成多个部分,这些部分会同时在多个线程上处理,最后将部分结果合并起来得到最终结果。

我们可以通过调用 Collection 的 parallelStream() 或 Stream 的 parallel() 方法来获取一个并行流。

代码示例

public class Test {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        Stream<Integer> parallelStream = list.parallelStream();

        // forEach – 并行执行
        list.parallelStream().forEach(e -> doSomething(e));
        // filter – 并行过滤
        list.parallelStream().filter(e -> e > 2).forEach(e -> doSomething(e));
        // map – 并行映射
        list.parallelStream().map(e -> e * 2).forEach(e -> doSomething(e));
        // reduce – 并行归约
        Integer sum = list.parallelStream().reduce(0, (a, b) -> a + b);
        // sorted – 并行排序
        list.parallelStream().sorted().forEach(e -> doSomething(e));
    }
}

通过并行流可以让许多常见的流操作利用多核 CPU 进行并行计算,从而获得更高的性能。但是,并不意味着所有的流操作都适合并行化,如果流处理的程序本身不是 CPU 密集型的,那么并行化可能得不到太大的性能提升,甚至有所下降。

简单运用示例

forEach遍历输出

public class Test3 {
    public static void main(String[] args) {
        //集合的遍历
        ArrayList<Integer> list = new ArrayList<>();
        //添加元素
        Collections.addAll(list,1,2,3,4,5,6,7,8,9);
        for (Integer integer : list) {
            System.out.println(integer);
        }
        //将集合中的每一个元素带入到方法accept中
        list.forEach(str -> System.out.println(str));
        list.forEach(System.out::println);

        //还可以用forEach方法输出集合中的偶数
        list.forEach(elm -> {
            if(elm %2 == 0){
                System.out.println(elm);
            }
        });
    }
}

匿名内部类调用

函数式接口:只有一个方法的接口。只要是函数式接口,都可以通过匿名函数来实现。

public class Test {
    interface Demo{
        int method(String s);
    }
    public static void main(String[] args) {

        // 匿名内部类调用
        Demo demo = new Demo() {
            @Override
            public int method(String s) {
                return Integer.parseInt(s);
            }
        };
        int v = demo.method("111");

        // lambda表达式
        Demo demo2 = (s) -> {
            return Integer.parseInt(s);
        };
        int v2 = demo2.method("222");

        // 单行简写
        Demo demo3 = s -> Integer.parseInt(s);
        int v3 = demo3.method("333");
    }
}

Optional 处理

Optional<String> optional = Optional.ofNullable("Hello JDK21");
// 如果存在值则打印,否则执行其他操作
optional.ifPresent(str -> System.out.println(str));
optional.orElseGet(() -> "默认值"); // 无值时返回默认值

并行计算求和

public class Demo {
    public static void main(String[] args) {
        List<String> values = Arrays.asList("1","2","3","4");
        System.out.println(sum(values));
    }
    public static int sum(List<String> values){
       // mapToInt方法返回的是一个int的Stream,再次调用stream.sum()得到和
       return values.parallelStream().mapToInt(i -> Integer.parseInt(i)).sum();
    }
}

ArryList排序

public class Test1 {

    public static void main(String[] args) {
        /**
         * 需求:
         * 已知在ArryList中有若干个Person类对象,将这些Person对象按照年龄来降序排序
         */
        List<Person> list = new ArrayList<>();

        // 升序
        list.sort((o1,o2) -> o1.age - o2.age);
        // 降序
        list.sort((o1,o2) -> o2.age - o1.age);
        // 按name降序(借助String的compareTo)
        list.sort((o1, o2) -> o2.getName().compareTo(o1.getName()));

        // 按age升序排序(Lambda表达式)
        Collections.sort(list, (o1, o2) -> o1.getAge() - o2.getAge());
        // 按name字典序排序
        Collections.sort(list, (o1, o2) -> o1.getName().compareTo(o2.getName()));

        // 简化方法(推荐)
        // 按age升序(方法引用:Person::getAge)
        list.sort(Comparator.comparingInt(Person::getAge));
        // 按age降序(加.reversed())
        list.sort(Comparator.comparingInt(Person::getAge).reversed());

        // 按age升序,收集为新的List
        List<Person> sortedByAge = people.stream()
            .sorted(Comparator.comparing(Person::getAge))
            .collect(Collectors.toList());
        ///////////////////////////////////////////////////////////////////////////////////
        //对List<String>进行排序
        List<String> list = new ArrayList<String>();
        Collections.sort(list);
        Collections.sort(list, Collections.reverseOrder());
        // 自然排序(等效于 Comparator.naturalOrder())
        list.stream().sorted((a, b) -> a.compareTo(b)).forEach(System.out::println);
        // 倒序排序
        list.stream().sorted((a, b) -> b.compareTo(a)).forEach(System.out::println);

        //对List<Map<String, String>>进行降序
        List<Map<String, String>> list = new ArrayList<Map<String, String>>();
        Collections.sort(list, (o1, o2) -> {
            String str1 = o1.get("countScore").toString();
            String str2 = o2.get("countScore").toString();
            //str2在前,str1在后,默认降序
            return str2.compareTo(str1);
        });
    }
}

TreeSet排序

public class Test2 {
    public static void main(String[] args) {
        // 使用Lambda表达式来实现Comparator接口,并实例化一个TreeSet对象
        TreeSet<Person> set = new TreeSet<>((o1,o2) -> {
            if(o1.age >= o2.age){
                return -1;
            }else {
                return 1;
            }
        });
        set.add(new Person("小蓝",10));
        set.add(new Person("小红",11));
        set.add(new Person("小紫",18));
        set.add(new Person("小橙",20));
        System.out.println(set);
    }
}

removeIf删除方法

public class Test4 {
    public static void main(String[] args) {
        List<Person> list = new ArrayList<>();
        list.add(new Person("小蓝",10));
        list.add(new Person("小红",11));
        list.add(new Person("小紫",10));
        list.add(new Person("小橙",20));

        //之前删除年龄大于10岁的元素  使用迭代器删除
        ListIterator<Person> it = list.listIterator();
        while (it.hasNext()){
            Person person = it.next();
            if (person.age > 10){
                it.remove();
            }
        }
        //使用lambda实现
        //将集合中的每一个元素都带入到test方法中,如果返回值是true将删除
        list.removeIf(t -> {
            if (t.age > 10){
                return true;
            }else {
                return false;
            }
        });
        //简化
        list.removeIf(t -> t.age>10);
        System.out.println(list);
    }
}

分组和分区(Collectors)

List<String> words = List.of("apple", "banana", "cat", "dog", "elephant");
// 按长度分组
Map<Integer, List<String>> groupByLength = words.stream().collect(Collectors.groupingBy(word -> word.length()));

// 按条件分区(长度是否大于3)
Map<Boolean, List<String>> partitionByLength = words.stream().collect(Collectors.partitioningBy(word -> word.length() > 3));

开辟线程

public class Test5 {
    public static void main(String[] args) {
        //使用lambda表达式开辟一个线程,做一个数字的输出
        Thread t = new Thread(() -> {
            for (int i =0; i<10; i++){
                System.out.println(i);
            }
        });
        t.start();
    }
}

Stream的使用

Stream流的filter使用

List<String> list = Arrays.asList("张三", "李四", "王五", "小六");

List<String> result = list.stream().filter(str -> !"李四".equals(str)).collect(Collectors.toList());
System.out.println("stream 过滤之后:" + result);

String result2 = list.stream().filter(str -> "李四".equals(str)).findAny().orElse("找不到!");
System.out.println("stream 过滤之后 2:" + result2);

Stream流的map使用

// 示例一:转换大写
List<String> list1 = Arrays.asList("zhangSan", "liSi", "wangWu");

List<String> list11 = list1.stream().map(String::toUpperCase).collect(Collectors.toList());
System.out.println("转换之后的数据:" + list11);

// 示例二:转换数据类型
List<String> list2 = Arrays.asList("1", "2", "3");

List<Integer> list22 = list2.stream().map(Integer::valueOf).collect(Collectors.toList());
System.out.println("转换之后的数据:" + list22);

// 示例三:获取平方
List<Integer> list3 = Arrays.asList(new Integer[] { 1, 2, 3, 4, 5 });
List<Integer> list33 = list3.stream().map(n -> n * n).collect(Collectors.toList());
System.out.println("平方的数据:" + list33);

// 示例四:list对象转list对象
// 将List<User>对象中的name属性转换为List<String>对象
List<String> listStr= user.stream().map(x -> x.getName()).collect(Collectors.toList());

// 将List<String>对象转换为List<User>对象
List<User> userList = listStr.stream().map(name-> {
    User user= new User();
    user.setName(name);
    user.setPassword(null);
    user.setAge(null);
    return user;
}).collect(Collectors.toList());
// 简写
List<User> userList = listStr.stream().map(name-> new User(name)).collect(Collectors.toList());

// 将List<User>对象转换为List<UserInfo>对象
List<UserInfo> collect = userList.stream().map(l -> new UserInfo(l.getName(), l.getPassword())).collect(Collectors.toList());

Stream流的sort使用

List<String> list = Arrays.asList("张三", "李四", "王五", "小六");
list.stream().limit(3).sorted().forEach(System.out::println);

// 先排序后比较效率更高
List<User> listUser = new ArrayList<User>();
listUser.stream().limit(3).sorted((u1, u2) -> u1.getName().compareTo(u2.getName())).collect(Collectors.toList());

Stream流的max/min/distinct使用

// 示例一:取最大/最小值
List<String> list13 = Arrays.asList("zhangsan","lisi","wangwu","xuwujing");
int maxLines = list13.stream().mapToInt(String::length).max().getAsInt();
int minLines = list13.stream().mapToInt(String::length).min().getAsInt();
System.out.println("最长字符的长度:" + maxLines+",最短字符的长度:"+minLines);

// 示例二:得到去重之后的数据
String lines = "good good study day day up";
List<String> list14 = new ArrayList<String>();
list14.add(lines);
List<String> words = list14.stream().flatMap(line -> Stream.of(line.split(" "))).filter(word -> word.length() > 0).map(String::toLowerCase).distinct().sorted().collect(Collectors.toList());
System.out.println("去重复之后:" + words);

Stream流的reduce使用

reduce 主要作用是把 Stream 元素组合起来进行操作。

// 示例一:字符串连接
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println("字符串拼接:" + concat);

// 示例二:得到最小值
double minValue = Stream.of(-4.0, 1.0, 3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
System.out.println("最小值:" + minValue);

// 示例三:求和
// 计算总和
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
// 求和, 无起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println("有无起始值求和:" + sumValue);

// 求和, 有起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(1, Integer::sum);
System.out.println("有起始值求和:" + sumValue);

// 示例四:过滤拼接
concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
System.out.println("过滤和字符串连接:" + concat);

Stream流的Match使用

  • allMatch:Stream 中全部元素符合则返回 true ;
  • anyMatch:Stream 中只要有一个元素符合则返回 true;
  • noneMatch:Stream 中没有一个元素符合则返回 true。
// 检查数据是否符合
boolean all = lists.stream().allMatch(u -> u.getId() > 3);
System.out.println("是否都大于3:" + all);

boolean any = lists.stream().anyMatch(u -> u.getId() > 3);
System.out.println("是否有一个大于3:" + any);

boolean none = lists.stream().noneMatch(u -> u.getId() > 3);
System.out.println("是否没有一个大于3的:" + none);

专用流

在 JDK 中,除了通用的 Stream 外,还提供了针对基本数据类型的专用流(IntStream、LongStream、DoubleStream),它们在处理数值型数据时更高效(避免自动装箱 / 拆箱),并提供了更多数值相关的操作方法。以下是这些流的常用操作示例:

创建流

// 1. 从值创建
IntStream intStream = IntStream.of(1, 2, 3, 4);
LongStream longStream = LongStream.of(10L, 20L, 30L);

// 2. 范围创建(左闭右开)
IntStream rangeStream = IntStream.range(1, 5); // 1,2,3,4
IntStream rangeClosedStream = IntStream.rangeClosed(1, 5); // 1,2,3,4,5

// 3. 从数组创建
int[] nums = {1, 2, 3};
IntStream arrayStream = IntStream.of(nums);

// 4. 随机数流(生成5个0-100的随机整数)
IntStream randomStream = new Random().ints(5, 0, 100);

过滤与映射

// 过滤偶数
IntStream.of(1, 2, 3, 4, 5)
         .filter(n -> n % 2 == 0)
         .forEach(System.out::println); // 2,4

// 映射(将int转换为long)
IntStream.of(1, 2, 3)
         .mapToLong(n -> (long) n * 10)
         .forEach(System.out::println); // 10,20,30

排序与去重

// 排序
IntStream.of(3, 1, 4, 2)
         .sorted()
         .forEach(System.out::println); // 1,2,3,4

// 去重
IntStream.of(1, 2, 2, 3, 3, 3)
         .distinct()
         .forEach(System.out::println); // 1,2,3

限制与跳过

// 取前3个元素
IntStream.rangeClosed(1, 10)
         .limit(3)
         .forEach(System.out::println); // 1,2,3

// 跳过前3个元素
IntStream.rangeClosed(1, 10)
         .skip(3)
         .forEach(System.out::println); // 4,5,6,7,8,9,10

聚合计算

// 求和
int sum = IntStream.of(1, 2, 3).sum(); // 6

// 求最大值(返回OptionalInt)
int max = IntStream.of(1, 2, 3).max().orElse(0); // 3

// 求最小值
int min = IntStream.of(1, 2, 3).min().orElse(0); // 1

// 求平均值(返回OptionalDouble)
double avg = IntStream.of(1, 2, 3).average().orElse(0); // 2.0

// 统计信息(总和、数量、平均值等)
IntSummaryStatistics stats = IntStream.of(1, 2, 3, 4).summaryStatistics();
System.out.println("总和: " + stats.getSum()); // 10
System.out.println("数量: " + stats.getCount()); // 4
System.out.println("平均值: " + stats.getAverage()); // 2.5

匹配操作

// 是否全部满足条件
boolean allEven = IntStream.of(2, 4, 6).allMatch(n -> n % 2 == 0); // true

// 是否存在满足条件的元素
boolean hasOdd = IntStream.of(2, 3, 4).anyMatch(n -> n % 2 != 0); // true

// 是否全部不满足条件
boolean noneNegative = IntStream.of(1, 2, 3).noneMatch(n -> n < 0); // true

转换为集合/数组

// 转换为List(需装箱为Integer)
List<Integer> list = IntStream.of(1, 2, 3)
                              .boxed() // 转换为Stream<Integer>
                              .toList();

// 转换为数组
int[] array = IntStream.of(1, 2, 3).toArray();

累加器(reduce)

// 计算1-5的和(初始值0,累加逻辑:a + b)
int sum = IntStream.rangeClosed(1, 5)
                   .reduce(0, (a, b) -> a + b); // 15

// 计算阶乘(5! = 120)
int factorial = IntStream.rangeClosed(1, 5)
                         .reduce(1, (a, b) -> a * b); // 120

并行流(提高大数据量处理效率)

// 并行计算1-1000000的和(自动利用多线程)
long sum = IntStream.rangeClosed(1, 1_000_000)
                    .parallel() // 转换为并行流
                    .sum();

双冒号之方法引用

Integer::parseInt;    // 方法引用
Integer::new;         // 引入构造方法

interface Demo{
    int method(String s);
}
public class TestTwo {

    public static void main(String[] args) {

        // 匿名内部类调用
        Demo demo1 = new Demo() {
            @Override
            public int method(String s) {
                return Integer.parseInt(s);
            }
        };
        int v1 = demo1.method("111");
        System.out.println(v1);

        // Lambda表达式
        Demo demo2 = s -> {
            return Integer.parseInt(s);
        };
        int v2 = demo2.method("222");
        System.out.println(v2);

        // Lambda表达式 单行简写
        Demo demo3 = s -> Integer.parseInt(s);
        int v3 = demo3.method("333");
        System.out.println(v3);

        // 方法引用 双冒号::
        Demo demo4 = Integer::parseInt;
        int v4 = demo4.method("444");
        System.out.println(v4);

        // 引入构造方法
        Demo demo5 = Integer::new;
        int v5 = demo5.method("555");
        System.out.println(v5);

         // 排序示例
        List<Integer> list = Arrays.asList(20, 53, 16, 95, 100);
        // 方式一:匿名内部类
        list.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        });
        // 方式二:Lambda表达式
        list.sort((x,y) -> Integer.compare(x,y));

        // 方式三:静态方法引用
        list.sort(Integer::compare);

        // 打印
        list.forEach(System.out::println);
    }
}
posted @ 2022-09-15 16:35  盗梦笔记  阅读(300)  评论(0)    收藏  举报