輸入字符串 "abcedsadgsadfasdfaa",輸出每個字符及其出現次數,按字符自然序排序:{a=6, b=1, c=1, d=3, e=2, f=2, g=1, s=3}。
public static Map countCharsTreeMap(String str) {
if (str == null || str.isEmpty()) return Collections.emptyMap();
Map freq = new TreeMap<>(); // 自然序:'0'-'9' < 'A'-'Z' < 'a'-'z'
for (char c : str.toCharArray()) {
freq.merge(c, 1, Integer::sum); // JDK 8+ 語法糖:存在則累加,否則放入 1
}
return freq;
}
countCharsStream(String str) { return str == null ? Collections.emptyMap() : str.chars() // IntStream .mapToObj(c -> (char) c) // Stream .collect(Collectors.groupingBy( c -> c, TreeMap::new, // 保持有序 Collectors.counting() // Long 計數 )); }
public static int[] countCharsArray(String str) {
// 假設只統計 BMP 字符 (0~65535),內存 128KB
int[] cnt = new int[Character.MAX_VALUE + 1];
for (char c : str.toCharArray()) cnt[c]++;
return cnt; // 後續遍歷 cnt[i] > 0 輸出
}
| 實現 | 平均耗時 (ms) | 內存分配 | 優點 | 缺點 |
|---|---|---|---|---|
| TreeMap + merge | ~45 | 中等 | 有序、API 簡潔、泛型安全 | 自動裝箱、樹結構開銷 |
| Stream + groupingBy | ~60 | 較高 | 函數式、易並行 .parallel() | 中間對象多、Lambda 開銷 |
| int[] 直接計數 | ~3 | 固定 256KB | 極快、無裝箱、無鎖 | 需預知字符範圍、補碼對需特殊處理 |
import java.util.*;
public class CharFrequency {
public static void main(String[] args) {
String input = "abcedsadgsadfasdfaa";
System.out.println("=== TreeMap 版 ===");
Map m1 = countCharsTreeMap(input);
m1.forEach((k, v) -> System.out.print(k + "=" + v + " "));
System.out.println();
System.out.println("\n=== Stream 版 ===");
Map m2 = countCharsStream(input);
m2.forEach((k, v) -> System.out.print(k + "=" + v + " "));
System.out.println();
System.out.println("\n=== 陣列版 (輸出非零) ===");
int[] arr = countCharsArray(input);
for (int i = 0; i < arr.length; i++) {
if (arr[i] > 0) System.out.print((char)i + "=" + arr[i] + " ");
}
}
// 三個方法實現見上文
}
str.toLowerCase().chars()...
str.codePoints() // IntStream of code points
.collect(Collectors.groupingBy(cp -> cp, TreeMap::new, Collectors.counting()));
freq.entrySet().stream()
.sorted(Map.Entry.comparingByValue().reversed())
.limit(10)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a,b)->a, LinkedHashMap::new));
TreeMap + merge,代碼清晰、自動有序、無需並行。int[] 或 long[] 直接計數,配合位圖/羅賓漢哈希優化內存。Stream.parallel() + ConcurrentHashMap,或分片後合併。codePoints() 而非 chars(),否則 Emoji 會被拆成兩個代理項。