博客 / 詳情

返回

Java 8 總結

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 實例來執行任務

示例:使用默認線程池和自定義線程池
如感興趣,點贊加關注,謝謝!!!

user avatar thinkfault 頭像
1 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.