3

1、Lambda

Lambda 表达式是 Java 8 引入的一种新特性,允许你以更加简洁的方式编写匿名函数,从而使代码更简洁和易读。Lambda 表达式的语法格式如下:

(parameters) -> expression
(parameters) -> { statements; }

1.1、基本语法

  1. 无参数

    () -> System.out.println("Hello, World!");
  2. 一个参数,无需括号
    x -> x * x
  3. 多个参数,需要括号
    (a, b) -> a + b
  4. 带类型的多个参数
    (int a, int b) -> a + b
  5. 多行语句,需要大括号

    (int a, int b) -> {
        int sum = a + b;
        return sum;
    }

1.2、使用示例

  1. 使用 Lambda 表达式代替匿名类
    传统的匿名内部类:
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, World!");
    }
}).start();

使用 Lambda 表达式:

new Thread(() -> System.out.println("Hello, World!")).start();
  1. 使用 Lambda 表达式进行集合操作
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));
  1. 使用 Lambda 表达式进行排序
    传统方式:
Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});

使用 Lambda 表达式:

Collections.sort(names, (a, b) -> a.compareTo(b));

2、与函数式接口结合

Lambda 表达式通常与函数式接口结合使用。函数式接口是仅包含一个抽象方法的接口,可以使用 @FunctionalInterface 注解来标识。例如:

@FunctionalInterface
interface MyFunctionalInterface {
    void doSomething();
}

public class LambdaExample {
    public static void main(String[] args) {
        MyFunctionalInterface func = () -> System.out.println("Doing something");
        func.doSomething();
    }
}
public static int sum(int num1, int num2, Accumulator accumulator) {
        return accumulator.add(num1, num2);
    }

    @FunctionalInterface
    public interface Accumulator {
        int add(int o1, int o2);
    }

public static void main(String[] args) {
        Runnable runnable = () -> System.out.println("hello world");
        new Thread(runnable).start();

        List<Integer> nums = Arrays.asList(1, 2, 3);
        List<Integer> doubleNums = nums.stream().map(x -> x * x).collect(Collectors.toList());

        int result = sum(1,2, (a, b) -> a + b);
        System.out.println(result);

    }

3、常见的函数式接口

java 8 提供了一些常见的函数式接口,可以直接用于 Lambda 表达式:

  • Consumer<T>:接受一个输入参数并且不返回结果的操作
Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello");
  • Supplier<T>:无输入参数但返回一个结果的操作
Supplier<String> supplier = () -> "Hello";
System.out.println(supplier.get());
  • Function<T, R>:接受一个输入参数并返回一个结果的操作
Function<Integer, String> intToString = i -> "Number: " + i;
System.out.println(intToString.apply(5));
  • Predicate<T>:接受一个输入参数并返回一个布尔值的操作
Predicate<String> isEmpty = s -> s.isEmpty();
System.out.println(isEmpty.test("Hello")); // false

4、变量作用域

Lambda 表达式可以使用外部的局部变量,但这些变量必须是最终的或实际上的最终变量(即在 Lambda 表达式中不能修改这些变量)

String greeting = "Hello";
Consumer<String> greeter = name -> System.out.println(greeting + ", " + name);
greeter.accept("World");
// greeting = "Hi"; // 编译错误

5、新的日期和时间 API

java 8 引入了全新的日期和时间 API (java.time 包),提供了更好用和更灵活的日期和时间处理方法

LocalDate today = LocalDate.now();
System.out.println("today :" + today.toString());
LocalDate date = LocalDate.of(1989, Month.MARCH, 23);
System.out.println("date : " + date.toString());
LocalDateTime dateTime = LocalDateTime.now();
System.out.println("dateTime : " + dateTime);

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatDateTime = dateTime.format(formatter);
System.out.println("formatDateTime : " + formatDateTime);

输出 :

today :2024-07-09
date : 1989-03-23
dateTime : 2024-07-09T10:03:56.416
formatDateTime : 2024-07-09 10:03:56

6、Stream API使用

数据模拟

List<Person> peoples = Arrays.asList(
                new Person("Alice", 30, "New York1"),
                new Person("Alice", 31, "New York2"),
                new Person("Bob", 20, "San Francisco"),
                new Person("Charlie", 25, "Los Angeles"),
                new Person("David", 15, "Chicago"),
                new Person("Eve", 35, "Boston")
        );

6.1、过滤年龄大于30的姓名,同时大写转换为List

List<String> result = peoples.stream()
                .filter(person -> person.getAge() >= 30)
                .map(Person::getName)
                .map(String::toUpperCase)
                .sorted()
                .collect(Collectors.toList());

result.forEach(System.out::println);

6.2、分组

Map<String, List<Person>> namePersons = peoples.stream()
                .collect(Collectors.groupingBy(Person::getName));
namePersons.forEach((name, persons) -> {
    System.out.println(name + ":" + persons);
});

6.3、List转换为Map

无name重复的场景

Map<String, Person> personMap = peoples.stream()
                .collect(Collectors.toMap(Person::getName, person -> person));
System.out.println(personMap);

有name重复的场景,处理键冲突(例如 : 使用相同的键合并值)

Map<String, Person> nameToPersonMapWithMerge = peoples.stream()
                .collect(Collectors.toMap(
                        Person::getName,
                        person -> person,
                        (existing, replacement) -> replacement
                ));
nameToPersonMapWithMerge.forEach((name, person) -> {
    System.out.println(name + ":" + person);
});

7、Optional

Optional 类是 Java 8 引入的一个重要特性,用于处理可能为空(null)的值。它通过显式地表示值可能存在或不存在,从而减少了 NullPointerException 的风险,并使代码更加易读和易维护

Optional<Address> address = Optional.ofNullable(new Address("New York", "5th Avenue"));
Person person = new Person("John", address);

// 使用 map 和 flatMap 获取嵌套值
Optional<String> city = person.getAddress()
                              .map(Address::getCity);
city.ifPresent(System.out::println); // 输出:New York

// 使用 orElse 提供默认值
String street = person.getAddress()
                      .map(Address::getStreet)
                      .orElse("Unknown Street");
System.out.println(street); // 输出:5th Avenue

// 使用 orElseThrow 抛出异常
String cityName = person.getAddress()
                        .map(Address::getCity)
                        .orElseThrow(() -> new IllegalArgumentException("City not found"));
System.out.println(cityName); // 输出:New York

// 处理空的 Optional
Optional<Address> emptyAddress = Optional.empty();
Person personWithoutAddress = new Person("Jane", emptyAddress);

String cityNameOrDefault = personWithoutAddress.getAddress()
                                              .map(Address::getCity)
                                              .orElse("Default City");
System.out.println(cityNameOrDefault); // 输出:Default City

8、map computeIfPresent & computeIfAbsent

Map<String, Integer> maps = new HashMap<>();
// 使用 computeIfAbsent 插入值,computeIfAbsent 方法在键缺失时进行计算并插入值
maps.computeIfAbsent("Apple", key -> 10);
maps.computeIfAbsent("Banana", key -> 20);

// 使用 computeIfPresent 更新值,computeIfPresent 方法在键存在时更新值
maps.computeIfPresent("Apple", (key, value) -> value * 2);
maps.computeIfPresent("Banana", (key, value) -> value + 5);

maps.forEach((key, value) -> System.out.println(key + ":" + value));

9、CompletableFuture

CompletableFuture 是 Java 8 中引入的一个类,用于支持异步编程和并发任务。它不仅实现了 Future 接口,还提供了许多用于任务组合和处理的方法。理解 CompletableFuture 的底层原理有助于更好地使用和优化它

9.1、基本结构

CompletableFuture 通过一系列内部状态来跟踪任务的执行状态。主要的状态有:

  • 未完成 (Incomplete): 任务尚未完成
  • 正常完成 (Normal completion): 任务已经成功完成
  • 异常完成 (Exceptional completion): 任务因异常而失败

这些状态由一个内部的 volatile 变量来管理,该变量确保多线程环境中的可见性和一致性

9.1、任务执行

CompletableFuture 可以通过以下几种方式执行任务:

  • 默认线程池: 默认情况下,CompletableFuture 使用一个由 ForkJoinPool.commonPool() 提供的公共线程池。这个线程池是一个全局的、共享的、并发的线程池,适用于大多数异步任务
  • 自定义线程池: 你也可以指定一个自定义的线程池,通过提供一个 Executor 实例来执行任务

示例:使用默认线程池和自定义线程池
如感兴趣,点赞加关注,谢谢!!!


journey
32 声望22 粉丝