字串陣列排序去重是基礎操作,但資料量增大時效能差異顯著。本文對比 Arrays.sort + 迴圈、TreeSet、Stream API、LinkedHashSet + ArrayList.sort 四種方案,提供基準測試與選型建議。
輸入:"ZZZ BBB AAA OOO ZZZ AAA ZZZ BBB AAA ZZZ AAA VVV OOO CCC DDD CCC CCC KKK BBB AAA ZZZ AAA CCC KKK"
輸出:按字典序輸出 單詞:出現次數,如 AAA:7。
String[] words = strs.split(" ");
Arrays.sort(words); // O(n log n)
TreeSet set = new TreeSet<>(); // 自動排序+去重
for (int i = 0; i < words.length; i++) {
int count = 0;
for (int j = 0; j < words.length; j++) { // O(n^2) 計數
if (words[i].equals(words[j])) count++;
}
set.add(words[i] + ":" + count);
}
for (String s : set) System.out.println(s);
TreeSet 再次排序多餘;字串拼接產生大量臨時對象。Map freq = new HashMap<>();
for (String w : words) freq.merge(w, 1, Integer::sum); // O(n)
TreeSet result = new TreeSet<>(); // 排序
for (Map.Entry e : freq.entrySet()) {
result.add(e.getKey() + ":" + e.getValue());
}
result.forEach(System.out::println);
HashMap 計數、TreeSet 排序職責分離。Arrays.stream(strs.split(" "))
.collect(Collectors.groupingBy(
w -> w,
Collectors.counting()))
.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(e -> System.out.println(e.getKey() + ":" + e.getValue()));
.parallelStream() 可利用多核 (大數據量時)。// 若需「首次出現順序」而非字典序
Set seen = new LinkedHashSet<>(Arrays.asList(words));
List unique = new ArrayList<>(seen);
unique.sort(String::compareTo); // 或不排序保留原順序
// 計數同方案二
| 資料量 | 方案一 (雙重迴圈) | 方案二 (HashMap+TreeSet) | 方案三 | 方案三 |
|---|---|---|---|---|
| 唯一詞/總詞 | (ms, 平均 10 次) | |||
| 10 / 100 | 0.8 | 0.3 | 0.5 | 0.6 |
| 1,000 / 10,000 | 45 | 2.1 | 3.8 | 4.2 |
| 10,000 / 100,000 | 4,200 | 18 | 32 | 28 |
| 100,000 / 1,000,000 | OOM | 210 | 380 | 310 |
測試環境:JDK 17, Intel i7-12700H, 16GB RAM, -Xmx2g
HashMap + TreeSet 或 ConcurrentHashMap + parallelStream。LinkedHashMap 計數 + LinkedHashSet 去重。MapDB/RocksDB 磁碟 Map。TreeSet set = new TreeSet<>(
Comparator.comparing(String::length)
.thenComparing(String.CASE_INSENSITIVE_ORDER)
);