首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 函數式程式設計(三):Stream map 操作與列表轉換進階實戰

     編輯:Java 相關知識

列表轉換的核心:map 操作詳解

Stream.map(Function<T,R>) 將流中每個元素映射為新元素,形成新流,元素數量不變但類型可變。這是函數式編程中「函子」概念在 Java 的體現。

基本用法:字串大寫轉換

List friends = Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott");

// 命令式:可變集合、外部迭代
List uppercase = new ArrayList<>();
for (String name : friends) uppercase.add(name.toUpperCase());

// 函數式:不可變、內建迭代、管道式
friends.stream()
    .map(String::toUpperCase)           // 方法引用
    .forEach(System.out::println);      // 終端操作觸發執行

// 輸出:BRIAN NATE NEAL RAJU SARA SCOTT

類型轉換:String → Integer

friends.stream()
    .map(String::length)                // String -> Integer
    .forEach(len -> System.out.print(len + " "));

// 輸出:5 4 4 4 4 5

map 進階模式

1. 複雜物件轉換:DTO 映射

record User(String name, int age, String email) {}
record UserDTO(String name, boolean adult, String domain) {}

List users = List.of(
    new User("Alice", 25, "alice@gmail.com"),
    new User("Bob", 17, "bob@yahoo.com")
);

List dtos = users.stream()
    .map(u -> new UserDTO(
        u.name(),
        u.age() >= 18,
        u.email().substring(u.email().indexOf('@') + 1)
    ))
    .toList();

2. 異常處理包裝:Checked Exception 在 Lambda 中

@FunctionalInterface
interface ThrowingFunction {
    R apply(T t) throws Exception;
}

static  Function wrap(ThrowingFunction f) {
    return t -> {
        try { return f.apply(t); }
        catch (Exception e) { throw new RuntimeException(e); }
    };
}

// 使用
List urls = List.of("https://a.com", "https://b.com");
List docs = urls.stream()
    .map(wrap(url -> Jsoup.connect(url).get())) // Jsoup 拋出 IOException
    .toList();

3. 條件映射:map + filter 組合 vs flatMap

// 只轉換滿足條件的元素,其餘保持原樣
List mixed = List.of("apple", "BANANA", "Cherry");
List result = mixed.stream()
    .map(s -> s.equals(s.toUpperCase()) ? s.toLowerCase() : s.toUpperCase())
    .toList(); // 全部大小寫反轉

// 只有大寫才轉小寫,其他不變
List result2 = mixed.stream()
    .map(s -> s.equals(s.toUpperCase()) ? s.toLowerCase() : s)
    .toList();

方法引用 vs Lambda:選擇指南

「傳遞參數給方法」的 Lambda 應改用方法引用(俗稱「外包模式」):

場景Lambda方法引用建議
單純轉發x -> x.toUpperCase()String::toUpperCase方法引用
需額外參數/邏輯x -> transform(x, config)不可用Lambda
建構子x -> new DTO(x)DTO::new方法引用
靜態方法x -> Util.parse(x)Util::parse方法引用

效能考量:惰性與融合優化

  • 惰性求值:中間操作(map/filter)不執行,終端操作(collect/forEach)才一次性遍歷
  • 迴圈融合:JVM 可將多個 map/filter 融合為單一迴圈,避免中間集合分配
  • 並行流:.parallelStream() 自動分割任務至 ForkJoinPool,無共享狀態時線性加速
  • 基礎型別流:IntStream/LongStream/DoubleStream 避免裝箱拆箱,數值計算效能大增
// 數值計算優先用基本型別流
double avg = prices.stream()
    .mapToDouble(BigDecimal::doubleValue) // 轉為 DoubleStream
    .filter(p -> p > 20)
    .map(p -> p * 0.9)
    .average()
    .orElse(0.0);

常見陷阱與最佳實踐

  1. 避免副作用:map 中不要修改外部狀態、寫日誌、存資料庫
  2. 注意 null:上游可能產生 null,下游 map 會拋 NPE,用 Optional 或 filter(Objects::nonNull)
  3. 不要濫用 forEach:forEach 是終端操作且無返回值,偏好 collect/toList 保持管道可組合
  4. 基本型別優先:數值密集型運算用 mapToInt/Double/Long
  5. 大數據量分頁:配合 Spliterator 或資料庫游標,避免一次性載入記憶體

總結

map 是 Stream 轉換的核心,掌握其與 filter、flatMap、reduce 的組合模式,能解決 90% 的集合處理需求。記住三原則:不可變、無副作用、類型清晰,讓編譯器與 JVM 幫你優化。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved