Stream.flatMap构建词频矩阵的核心优势是结构清晰、可扩展、内存可控:先分词再展平成单词流,链式过滤停用词与标准化,再用groupingBy统计频次,支持分块处理与并行优化。

用 Stream.flatMap 构建词频矩阵,关键不在“快”,而在“结构清晰、可扩展、内存可控”。它本身不提速计算,但能干净地把分词→展平→统计三步串成一条惰性流水线,避免中间集合膨胀,这对大规模文本变量(比如上万条句子、每条含几十到上百词)特别实用。
先分词再展平:让每句话变成一串单词流
词频矩阵的起点是把原始文本列表(如 List)转为所有单词的扁平流。flatMap 正是干这个的:
- 对每句字符串调用
split(" ")得到String[] - 用
Arrays.stream(...)把数组转成子流 - flatMap 自动把所有子流“接起来”,产出一个
Stream,里面全是单词,顺序和原文一致
示例代码:
Stream<string> allWords = sentences.stream()
.flatMap(sentence -> Arrays.stream(sentence.toLowerCase().split("\W+")));</string>去停用词 + 小写标准化:在展平后立即过滤
别等展平完再遍历过滤——那会多一次遍历。直接把 filter 接在 flatMap 后面,利用 Stream 惰性求值特性,边展平边筛:
- 预定义
SetSTOP_WORDS = Set.of("the", "a", "an", "and", "or") - 在 flatMap 后加
.filter(word -> word.length() > 1 && !STOP_WORDS.contains(word)) - 这样每个单词只被创建、检查一次,不存临时列表
用 Collectors.groupingBy 统计频次
展平+清洗后的单词流,直接交给 Collectors.groupingBy(..., Collectors.counting()),一步生成 Map:
- 这是词频向量的基础结构,后续可转为稀疏矩阵(如用
Int2LongOpenHashMap)、或导出为 TF-IDF 特征 - 若需限制词汇表大小(比如只取前 10000 高频词),可在 groupingBy 后接
.entrySet().stream().sorted(Map.Entry.comparingByValue().reversed()).limit(10000)
应对超长文本:配合分块与并行流
单条句子本身很长(如整篇论文),或总文本量极大时,注意两点:
- 避免
split一次性生成巨量小字符串——改用Pattern.compile("\W+").splitAsStream(...),更省内存且支持正则 - 如果句子列表本身可并行处理(无状态依赖),用
sentences.parallelStream()开启并行;但注意groupingBy默认非线程安全,应搭配Collectors.toConcurrentMap或显式加ConcurrentHashMap收集器











