Can be replaced with 'Collectors.joining'

Can be replaced with ‘Collectors.joining’

Can be replaced with ‘Collectors.joining’

Can be replaced with ‘Collectors.joining’

這個警告是在提醒你,當前使用的字串拼接或累加邏輯可以用 Collectors.joining()
來更簡潔、更高效地實現。

  1. 使用 StringBuilder 在 Stream 中拼接
    // ❌ 可以改善的寫法
    StringBuilder sb = new StringBuilder();
    list.stream().forEach(item -> sb.append(item).append(“, “));
    String result = sb.toString();

// ✅ 使用 Collectors.joining()
String result = list.stream()
.collect(Collectors.joining(“, “));
2. 使用 reduce() 進行字串累加
// ❌ 效率不佳的寫法
String result = list.stream()
.reduce(“”, (a, b) -> a + “, “ + b);

// ✅ 更好的寫法
String result = list.stream()
.collect(Collectors.joining(“, “));
3. 傳統的迴圈拼接
// ❌ 傳統寫法
Listnames = Arrays.asList(“張三”, “李四”, “王五”);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < names.size(); i++) {
if (i > 0) {
sb.append(“, “);
}
sb.append(names.get(i));
}
String result = sb.toString();

// ✅ 簡潔的現代寫法
String result = names.stream()
.collect(Collectors.joining(“, “));
Collectors.joining() 的各種用法

  1. 基本用法 - 只有分隔符
    Listfruits = Arrays.asList(“蘋果”, “香蕉”, “橘子”);

// 結果: “蘋果,香蕉,橘子”
String result = fruits.stream()
.collect(Collectors.joining(“,”));
2. 有前綴和後綴
Listitems = Arrays.asList(“項目1”, “項目2”, “項目3”);

// 結果: “[項目1, 項目2, 項目3]”
String result = items.stream()
.collect(Collectors.joining(“, “, “[“, “]”));
3. 搭配 map() 轉換
Listusers = getUsers();

// 拼接使用者名稱
String userNames = users.stream()
.map(User::getName)
.collect(Collectors.joining(“, “));

// 拼接使用者 ID
String userIds = users.stream()
.map(user -> String.valueOf(user.getId()))
.collect(Collectors.joining(“,”));
4. 複雜物件的格式化拼接
Listproducts = getProducts();

// 結果: “商品: iPhone($999), 商品: Samsung($799), 商品: Pixel($699)”
String productList = products.stream()
.map(p -> String.format(“商品: %s($%d)”, p.getName(), p.getPrice()))
.collect(Collectors.joining(“, “));
實際應用場景

  1. 生成 SQL IN 子句
    ListuserIds = Arrays.asList(1L, 2L, 3L, 4L);

// ❌ 傳統寫法
StringBuilder inClause = new StringBuilder(“(“);
for (int i = 0; i < userIds.size(); i++) {
if (i > 0) inClause.append(“,”);
inClause.append(userIds.get(i));
}
inClause.append(“)”);

// ✅ 使用 joining()
String inClause = userIds.stream()
.map(String::valueOf)
.collect(Collectors.joining(“,”, “(“, “)”));

String sql = “SELECT * FROM users WHERE id IN “ + inClause;
// 結果: “SELECT * FROM users WHERE id IN (1,2,3,4)”
2. 生成 CSV 格式
Listemployees = getEmployees();

// 生成 CSV 行
String csvHeader = “姓名,部門,薪資”;
String csvData = employees.stream()
.map(emp -> String.join(“,”,
emp.getName(),
emp.getDepartment(),
String.valueOf(emp.getSalary())))
.collect(Collectors.joining(“\n”));

String fullCsv = csvHeader + “\n” + csvData;
3. 日誌訊息格式化
public void logUserActions(Listactions) {
String actionSummary = actions.stream()
.map(action -> String.format(“%s在%s執行了%s”,
action.getUserName(),
action.getTimestamp().format(DateTimeFormatter.ofPattern(“HH:mm”)),
action.getActionType()))
.collect(Collectors.joining(“; “));

logger.info("使用者行為摘要: {}", actionSummary);

}
4. HTML 元素生成
ListmenuItems = Arrays.asList(“首頁”, “產品”, “關於我們”, “聯絡我們”);

// 生成 HTML 選單
String htmlMenu = menuItems.stream()
.map(item -> String.format(“

  • %s
  • “, item))
    .collect(Collectors.joining(“\n”, “
      \n”, “\n
    “));

    System.out.println(htmlMenu);
    // 輸出:
    //


    效能考量

    1. joining() vs StringBuilder
      // ✅ joining() 內部已經優化,使用 StringBuilder
      ListlargeList = generateLargeStringList();

    // 推薦:Collectors.joining() 內部已經優化
    String result1 = largeList.stream()
    .collect(Collectors.joining(“,”));

    // 不推薦:手動 StringBuilder (除非有特殊需求)
    StringBuilder sb = new StringBuilder();
    largeList.forEach(s -> sb.append(s).append(“,”));
    String result2 = sb.toString();
    2. 避免重複的 String 操作
    // ❌ 效率低下 - 每次 + 都創建新字串
    String result = list.stream()
    .reduce(“”, (a, b) -> a + “,” + b);

    // ✅ 高效 - 內部使用 StringBuilder
    String result = list.stream()
    .collect(Collectors.joining(“,”));
    進階用法

    1. 條件式拼接
      Listitems = Arrays.asList(“”, “有效項目1”, null, “有效項目2”, “”);

    String result = items.stream()
    .filter(Objects::nonNull)
    .filter(s -> !s.trim().isEmpty())
    .collect(Collectors.joining(“, “));
    // 結果: “有效項目1, 有效項目2”
    2. 分組後拼接
    Listemployees = getEmployees();

    Map<String, String> departmentEmployees = employees.stream()
    .collect(Collectors.groupingBy(
    Employee::getDepartment,
    Collectors.mapping(Employee::getName, Collectors.joining(“, “))
    ));

    // 結果: {“IT”: “張三, 李四”, “HR”: “王五, 趙六”}
    3. 多層級拼接
    Map<String, List> productsByCategory = getProductsByCategory();

    String categoryList = productsByCategory.entrySet().stream()
    .map(entry -> {
    String category = entry.getKey();
    String products = entry.getValue().stream()
    .map(Product::getName)
    .collect(Collectors.joining(“, “));
    return category + “: “ + products;
    })
    .collect(Collectors.joining(“\n”));
    注意事項

    1. 處理空值
      ListlistWithNulls = Arrays.asList(“A”, null, “B”, null, “C”);

    // null 會被轉換為 “null” 字串
    String result1 = listWithNulls.stream()
    .collect(Collectors.joining(“,”));
    // 結果: “A,null,B,null,C”

    // 過濾掉 null 值
    String result2 = listWithNulls.stream()
    .filter(Objects::nonNull)
    .collect(Collectors.joining(“,”));
    // 結果: “A,B,C”
    2. 空集合處理
    ListemptyList = Arrays.asList();

    String result = emptyList.stream()
    .collect(Collectors.joining(“, “, “[“, “]”));
    // 結果: “[]” (前綴和後綴仍會保留)
    使用 Collectors.joining() 不僅讓程式碼更簡潔,也更有表達力,同時效能也更好!