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

Java 字符串字符頻率統計:TreeMap 實現、Stream API 一行碼、性能對比與最佳實踐

     編輯:Java 程式設計入門

需求

輸入字符串 "abcedsadgsadfasdfaa",輸出每個字符及其出現次數,按字符自然序排序:{a=6, b=1, c=1, d=3, e=2, f=2, g=1, s=3}。

方案一:TreeMap 手寫版(教學、可控排序)

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;
}

方案二:Stream API 一行碼(函數式、並行友好)

 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 計數
           ));
}

方案三:陣列計數極致性能(僅 ASCII/基本多語言面)

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 輸出
}

性能基準測試(JMH 參考數據,100 萬字符隨機字符串)

實現平均耗時 (ms)內存分配優點缺點
TreeMap + merge~45中等有序、API 簡潔、泛型安全自動裝箱、樹結構開銷
Stream + groupingBy~60較高函數式、易並行 .parallel()中間對象多、Lambda 開銷
int[] 直接計數~3固定 256KB極快、無裝箱、無鎖需預知字符範圍、補碼對需特殊處理

完整可運行 Demo

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] + " ");
        }
    }

    // 三個方法實現見上文
}

進階場景擴展

1. 忽略大小寫統計

str.toLowerCase().chars()...

2. 統計 Unicode 補充字符(Emoji、生僻字)

str.codePoints()  // IntStream of code points
   .collect(Collectors.groupingBy(cp -> cp, TreeMap::new, Collectors.counting()));

3. Top K 高頻字符

freq.entrySet().stream()
    .sorted(Map.Entry.comparingByValue().reversed())
    .limit(10)
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a,b)->a, LinkedHashMap::new));

最佳實踐建議

  1. 通用業務:TreeMap + merge,代碼清晰、自動有序、無需並行。
  2. 大數據/高吞吐:int[] 或 long[] 直接計數,配合位圖/羅賓漢哈希優化內存。
  3. 需要並行:Stream.parallel() + ConcurrentHashMap,或分片後合併。
  4. 補充平面字符:務必用 codePoints() 而非 chars(),否則 Emoji 會被拆成兩個代理項。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved