值得使用 Java Lambda 的 8 个场景

112 阅读4分钟

可能对于很多人来说,Lambda 显得陌生而复杂,认为 Lambda 会导致代码可读性下降,批评 Lambda 语法,甚至排斥它。

事实上,所有这些问题在尝试和熟悉之后可能都不是问题。

那些对 Lambda 持怀疑态度的人也许可以采取增量使用 Lambda 的策略。先尝试在一些简单、低风险的场景中使用 Lambda,然后逐步增加 Lambda 表达式的使用频率和范围。

迭代和过滤集合

使用Lambda表达式结合Stream API可以实现集合的遍历和过滤,代码更少,更加简洁,易于阅读。

List<String> letters= Arrays.asList("a", "b", "c");
for (String letter : letters) {
    if (letter.endsWith("c")) {
        System.out.println(letter);
    }
}

用 Lambda 重写:

List<String> letters= Arrays.asList("a", "b", "c");
letters.stream()
       .filter(letter -> letter.endsWith("c"))
       .forEach(System.out::println);

对集合的元素进行排序

使用 Lambda 表达式可以让您以更紧凑的形式将排序逻辑传递给排序方法,使代码更加简洁。

List<String> names = Arrays.asList("Aaron", "Bobby", "Charlotte");
Collections.sort(names, new Comparator<String>() {
    public int compare(String name1, String name2) {
        return name1.compareTo(name2);
    }
});

用 Lambda 重写:

List<String> names = Arrays.asList("Aaron", "Bobby", "Charlotte");
names.sort((name1, name2) -> name1.compareTo(name2));

集合上的聚合操作

Lambda表达式结合Stream API可以更优雅地实现对集合元素的聚合操作,例如求和、求平均等。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = 0;
for (Integer num : numbers) {
    sum += num;
}

用 Lambda 重写:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
                 .reduce(0, Integer::sum);

条件过滤和默认值设置

使用Lambda的Optional类可以更优雅地处理条件过滤和默认值设置的逻辑。

String name = "Aaron";
if (name != null && name.length() > 0) {
    System.out.println("Hello, " + name);
} else {
    System.out.println("Hello, Stranger");
}

用 Lambda 重写:

String name = "Aaron";
name = Optional.ofNullable(name)
               .filter(n -> n.length() > 0)
               .orElse("Stranger");
System.out.println("Hello, " + name);

简化匿名内部类

您可以简化代码并同时提高可读性。 举个创建线程的例子,传统的匿名内部类实现线程的语法比较冗长,而Lambda表达式可以用更简洁的方式达到同样的结果。

new Thread(new Runnable() {
    public void run() {
        System.out.println("Thread is running.");
    }
}).start();

用 Lambda 重写:

new Thread(() -> System.out.println("Thread is running.")).start();

new Thread(() -> {
    // do something
}).start();

集合元素的变换

使用Lambda的map方法可以更优雅地变换集合元素,提高代码可读性。

List<String> names = Arrays.asList("Aaron", "Bobby", "Charlotte");
List<String> uppercaseNames = new ArrayList<>();
for (String name : names) {
    uppercaseNames.add(name.toUpperCase());
}

用 Lambda 重写:

List<String> names = Arrays.asList("Aaron", "Bobby", "Charlotte");
List<String> uppercaseNames = names.stream()
                                   .map(String::toUpperCase)
                                   .collect(Collectors.toList());

集合的分组和计数

以更紧凑的形式传递分组和计数的逻辑,避免了匿名内部类的繁琐声明和实现。 通过groupingBy、counting、summingInt等方法,代码更加流畅、直观、优雅。

Grouping:

List<String> names = Arrays.asList("Aaron", "Bobby", "Charlotte");
Map<Integer, List<String>> namesGroupByLength = new HashMap<>();
for (String name : names) {
    int length = name.length();
    if (!namesGroupByLength.containsKey(length)) {
        namesGroupByLength.put(length, new ArrayList<>());
    }
    namesByLength.get(length).add(name);
}
System.out.println("Names grouped by length: " + namesGroupByLength);

用 Lambda 重写:

List<String> names = Arrays.asList("Aaron", "Bobby", "Charlotte");

Map<Integer, List<String>> namesGroupByLength = names.stream()
        .collect(Collectors.groupingBy(String::length));

System.out.println("Names grouped by length: " + namesGroupByLength);

Counting:

List<String> names = Arrays.asList("Aaron", "Bobby", "Charlotte");
int namesStartWithA = 0;
for (String name : names) {
    if (name.contains("A")) {
        namesStartWithA++;
    }
}
System.out.println("Number of names containing 'A': " + namesStartWithA);

用 Lambda 重写:

List<String> names = Arrays.asList("Aaron", "Bobby", "Charlotte");
long namesStartWithA = names.stream()
        .filter(name -> name.contains("A"))
        .count();
System.out.println("Number of names containing 'A': " + namesStartWithA);

大数据量集合的并行处理

当集合中的数据量较大时,很容易通过Lambda结合Stream API进行并行处理,以充分利用多核处理器,提高程序执行效率。 假设我们有一个包含一百万个整数的列表,并且我们想要计算这些整数的平均值:

List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
    numbers.add(ThreadLocalRandom.current().nextInt(100));
}

//
long startTimeSeq = System.currentTimeMillis();
double averageSequential = numbers.stream()
                                 .mapToInt(Integer::intValue)
                                 .average()
                                 .getAsDouble();
long endTimeSeq = System.currentTimeMillis();
System.out.println("Sequential Average: " + averageSequential);
System.out.println("Time taken (Sequential): " + (endTimeSeq - startTimeSeq) + "ms");


//Parallel
long startTimePar = System.currentTimeMillis();
double averageParallel = numbers.parallelStream()
                               .mapToInt(Integer::intValue)
                               .average()
                               .getAsDouble();
long endTimePar = System.currentTimeMillis();
System.out.println("Parallel Average: " + averageParallel);
System.out.println("Time taken (Parallel): " + (endTimePar - startTimePar) + "ms");


//Sequential Average: 59.4451232
//Time taken (Sequential): 10ms
//Parallel Average: 59.4451232
//Time taken (Parallel): 3ms

可以看出,顺序流和并行流获得相同的平均值,但并行流的处理时间明显小于顺序流。 这是因为并行流能够将任务拆分为多个较小的任务,并在多个处理器核心上同时执行这些任务。

Lambda 的使用场景远不止这些,可以灵活运用在多线程、文件操作等场景,熟悉后可以让代码更加简洁,编程更加精确优雅。

在编写代码时,改变偏见需要我们尝试并采取行动。 有时,我们可能对某种编程语言、框架或设计模式有偏见,认为它们不适合或不好用。 然而,只有尝试去理解和实践它们,我们才能真正知道它们的优点和缺点。