热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

高效处理大文件:单线程与多线程下的词频统计方法

本文探讨了在处理大文件时,如何通过单线程和多线程的方式使用Buffer流进行词频统计,以避免一次性加载文件导致的内存溢出问题,并提供了具体的实现代码。

一、背景介绍

当处理较小的文件时,可以直接将其加载到内存中进行处理。然而,对于大文件,如果尝试一次性加载全部内容,将会遇到内存不足的问题。例如,对于一个4GB的文本文件,尝试一次性读取会导致程序崩溃,如下图所示:

为了解决这一问题,我们需要采用分块读取的方法,逐步处理文件内容,避免内存溢出。

二、解决方案

2.1 单线程处理方案

单线程处理大文件时,可以使用缓冲流(BufferedInputStream)逐块读取文件内容,并在每次读取后立即进行词频统计。这种方式虽然简单,但效率较低,尤其是在处理非常大的文件时。下面是一个简单的实现示例:

public void singleThreadWordCount() throws IOException { BufferedInputStream in = new BufferedInputStream(new FileInputStream("word")); byte[] buf = new byte[4 * 1024]; int len = 0; Map total = new HashMap<>(); long start = System.currentTimeMillis(); while ((len = in.read(buf)) != -1) { byte[] bytes = Arrays.copyOfRange(buf, 0, len); String str = new String(bytes); StringTokenizer stringTokenizer = new StringTokenizer(str); while (stringTokenizer.hasMoreTokens()) { String strTemp = stringTokenizer.nextToken(); total.put(strTemp, total.getOrDefault(strTemp, 0) + 1); } } System.out.println(total.get("aabaa")); System.out.println("time: " + (System.currentTimeMillis() - start) + "ms"); }

上述代码展示了如何使用缓冲流逐块读取文件并进行词频统计。测试结果显示,单线程处理4GB文件的时间约为81740毫秒,统计到的词频次数为319783次。

2.2 多线程处理方案

为了提高处理大文件的效率,可以采用多线程的方式。通过将文件分成多个部分,每个部分由不同的线程独立处理,最后汇总各个线程的统计结果。这种方式可以显著提升处理速度。下面是一个多线程处理的示例:

public class MultiThreadWordCount { private static final ForkJoinPool pool = ForkJoinPool.commonPool(); public void run(String fileName, long chunkSize) throws ExecutionException, InterruptedException { File file = new File(fileName); long fileSize = file.length(); long position = 0; long start = System.currentTimeMillis(); ArrayList>> tasks = new ArrayList<>(); while (position > future = pool.submit(task); tasks.add(future); } HashMap totalMap = new HashMap<>(); for (Future> task : tasks) { HashMap map = task.get(); for (Map.Entry entry : map.entrySet()) { if (totalMap.containsKey(entry.getKey())) { totalMap.put(entry.getKey(), totalMap.get(entry.getKey()) + entry.getValue()); } else { totalMap.put(entry.getKey(), entry.getValue()); } } } System.out.println("time: " + (System.currentTimeMillis() - start) + "ms"); System.out.println(totalMap.get("aabaa")); } private static class CountTask implements Callable> { private final long start; private final long end; private final String fileName; public CountTask(String fileName, long start, long end) { this.start = start; this.end = end; this.fileName = fileName; } @Override public HashMap call() throws Exception { HashMap map = new HashMap<>(); FileChannel channel = new RandomAccessFile(this.fileName, "rw").getChannel(); MappedByteBuffer mbuf = channel.map(FileChannel.MapMode.READ_ONLY, this.start, this.end - this.start); String str = StandardCharsets.US_ASCII.decode(mbuf).toString(); StringTokenizer stringTokenizer = new StringTokenizer(str); while (stringTokenizer.hasMoreTokens()) { String strTemp = stringTokenizer.nextToken(); map.put(strTemp, map.getOrDefault(strTemp, 0) + 1); } return map; } } @Test public void testMultiThreadWordCount() throws ExecutionException, InterruptedException { MultiThreadWordCount counter = new MultiThreadWordCount(); counter.run("word", 1024 * 1024); } }

测试结果显示,多线程处理4GB文件的时间为115909毫秒,统计到的词频次数为320106次。尽管多线程处理的时间略长于单线程,但这主要是因为在合并各个线程的统计结果时存在一定的开销。通过调整分块大小,可以进一步优化性能。例如,将分块大小增加到20MB时,处理时间减少到26671毫秒,词频次数为320107次。

读者可以尝试调整分块大小,观察其对性能的影响。


推荐阅读
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • 本教程涵盖OpenGL基础操作及直线光栅化技术,包括点的绘制、简单图形绘制、直线绘制以及DDA和中点画线算法。通过逐步实践,帮助读者掌握OpenGL的基本使用方法。 ... [详细]
  • 尽管使用TensorFlow和PyTorch等成熟框架可以显著降低实现递归神经网络(RNN)的门槛,但对于初学者来说,理解其底层原理至关重要。本文将引导您使用NumPy从头构建一个用于自然语言处理(NLP)的RNN模型。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文基于刘洪波老师的《英文词根词缀精讲》,深入探讨了多个重要词根词缀的起源及其相关词汇,帮助读者更好地理解和记忆英语单词。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 本文探讨了如何在给定整数N的情况下,找到两个不同的整数a和b,使得它们的和最大,并且满足特定的数学条件。 ... [详细]
  • 本文详细介绍了Java中org.w3c.dom.Text类的splitText()方法,通过多个代码示例展示了其实际应用。该方法用于将文本节点在指定位置拆分为两个节点,并保持在文档树中。 ... [详细]
  • 基于KVM的SRIOV直通配置及性能测试
    SRIOV介绍、VF直通配置,以及包转发率性能测试小慢哥的原创文章,欢迎转载目录?1.SRIOV介绍?2.环境说明?3.开启SRIOV?4.生成VF?5.VF ... [详细]
  • 毕业设计:基于机器学习与深度学习的垃圾邮件(短信)分类算法实现
    本文详细介绍了如何使用机器学习和深度学习技术对垃圾邮件和短信进行分类。内容涵盖从数据集介绍、预处理、特征提取到模型训练与评估的完整流程,并提供了具体的代码示例和实验结果。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
author-avatar
知知亦不知_710
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有