Java代码实现
import java.util.*;
import java.util.stream.Collectors;
public class WordFrequencyCounter {
public static void main(String[] args) {
List<String> articles = Arrays.asList(
"This is the first article. This article is about Java.",
"The second article is about parallel streams in Java.",
"Java is a popular programming language."
);
Map<String, Long> wordFrequencyMap = articles.parallelStream()
.flatMap(s -> Arrays.stream(s.split("\\W+")))
.filter(word ->!word.isEmpty())
.collect(Collectors.groupingByConcurrent(String::toLowerCase, Collectors.counting()));
List<Map.Entry<String, Long>> topTenWords = wordFrequencyMap.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(10)
.collect(Collectors.toList());
topTenWords.forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
}
}
性能瓶颈分析及优化
- 内存消耗
- 瓶颈:在并行流处理过程中,尤其是处理非常大的字符串列表时,可能会产生大量的中间数据,例如拆分后的单词。如果内存不足,可能导致频繁的垃圾回收甚至OutOfMemoryError。
- 优化:可以采用分块处理的方式,将大的字符串列表拆分成多个较小的块,分别进行并行处理,然后合并结果。这样可以减少内存的瞬时压力。
- 线程开销
- 瓶颈:并行流依赖于Fork/Join框架,创建和管理线程会带来一定的开销。如果任务粒度太小,线程开销可能会超过并行处理带来的性能提升。
- 优化:调整任务的粒度,例如增加每次处理的字符串块的大小,使得任务足够大以抵消线程创建和管理的开销。同时,可以通过
parallelStream
的parallelism
参数手动设置并行度,以适配不同的硬件环境。
- I/O操作
- 瓶颈:如果字符串列表是从文件或者网络读取的,I/O操作可能成为性能瓶颈。并行流的优势在于CPU计算,而I/O操作通常是阻塞的,无法充分利用并行性。
- 优化:使用异步I/O操作,例如Java NIO中的
AsynchronousSocketChannel
或AsynchronousFileChannel
,在I/O操作进行的同时,其他线程可以继续处理已经读取的数据。另外,可以在I/O读取和并行处理之间加入缓冲区,减少I/O操作的频率。