返回顶部
首页 > 资讯 > 前端开发 > VUE >怎么用Java高效读取大文件
  • 297
分享到

怎么用Java高效读取大文件

2024-04-02 19:04:59 297人浏览 八月长安
摘要

这篇文章主要讲解了“怎么用Java高效读取大文件”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用Java高效读取大文件”吧!内存读取第一个版本,阿粉采用

这篇文章主要讲解了“怎么用Java高效读取大文件”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用Java高效读取大文件”吧!

内存读取

第一个版本,阿粉采用内存读取的方式,所有的数据首先读读取到内存中,程序代码如下:

Stopwatch stopwatch = Stopwatch.createStarted(); // 将全部行数读取的内存中 List<String> lines = FileUtils.readLines(new File("temp/test.txt"), Charset.defaultCharset()); for (String line : lines) {     // pass } stopwatch.stop(); System.out.println("read all lines spend " + stopwatch.elapsed(TimeUnit.SECONDS) + " s"); // 计算内存占用 logMemory();

logMemory方法如下:

MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); //堆内存使用情况 MemoryUsage memoryUsage = memoryMXBean.getHeapMemoryUsage(); //初始的总内存 long totalMemorySize = memoryUsage.getInit(); //已使用的内存 long usedMemorySize = memoryUsage.getUsed();  System.out.println("Total Memory: " + totalMemorySize / (1024 * 1024) + " Mb"); System.out.println("Free Memory: " + usedMemorySize / (1024 * 1024) + " Mb");

上述程序中,阿粉使用 Apache Common-io  开源第三方库,FileUtils#readLines将会把文件中所有内容,全部读取到内存中。

这个程序简单测试并没有什么问题,但是等拿到真正的数据文件,运行程序,很快程序发生了 OOM。

之所以会发生 OOM,主要原因是因为这个数据文件太大。假设上面测试文件 test.txt总共有 200W 行数据,文件大小为:740MB。

通过上述程序读取到内存之后,在我的电脑上内存占用情况如下:

怎么用Java高效读取大文件

可以看到一个实际大小为 700 多 M 的文件,读到内存中占用内存量为 1.5G 之多。而我之前的程序,虚拟机设置内存大小只有 1G,所以程序发生了  OOM。

当然这里最简单的办法就是加内存呗,将虚拟机内存设置到 2G,甚至更多。不过机器内存始终有限,如果文件更大,还是没有办法全部都加载到内存。

不过仔细一想真的需要将全部数据一次性加载到内存中?

很显然,不需要!

在上述的场景中,我们将数据到加载内存中,最后不还是一条条处理数据。

所以下面我们将读取方式修改成逐行读取。

逐行读取

逐行读取的方式比较多,这里阿粉主要介绍两种方式:

  • BufferReader

  • Apache Commons IO

  • Java8 stream

BufferReader

我们可以使用 BufferReader#readLine 逐行读取数据。

try (BufferedReader fileBufferReader = new BufferedReader(new FileReader("temp/test.txt"))) {     String fileLineContent;     while ((fileLineContent = fileBufferReader.readLine()) != null) {         // process the line.     } } catch (FileNotFoundException e) {     e.printStackTrace(); } catch (IOException e) {     e.printStackTrace(); }

Apache Commons IOCommon-IO

中有一个方法  FileUtils#lineIterator可以实现逐行读取方式,使用代码如下:

Stopwatch stopwatch = Stopwatch.createStarted(); LineIterator fileContents = FileUtils.lineIterator(new File("temp/test.txt"), StandardCharsets.UTF_8.name()); while (fileContents.hasNext()) {     fileContents.nextLine();     //  pass } logMemory(); fileContents.close(); stopwatch.stop(); System.out.println("read all lines spend " + stopwatch.elapsed(TimeUnit.SECONDS) + " s");

这个方法返回一个迭代器,每次我们都可以获取的一行数据。

其实我们查看代码,其实可以发现 FileUtils#lineIterator,其实用的就是  BufferReader,感兴趣的同学可以自己查看一下源码

由于公号内无法插入外链,关注『Java极客技术』,回复『20200610』 获取源码

Java8 stream

Java8 Files 类新增了一个 lines,可以返回 Stream我们可以逐行处理数据。

Stopwatch stopwatch = Stopwatch.createStarted(); // lines(Path path, Charset cs) try (Stream<String> inputStream = Files.lines(Paths.get("temp/test.txt"), StandardCharsets.UTF_8)) {     inputStream             .filter(str -> str.length() > 5)// 过滤数据             .forEach(o -> {                 // pass do sample logic             }); } logMemory(); stopwatch.stop(); System.out.println("read all lines spend " + stopwatch.elapsed(TimeUnit.SECONDS) + " s");

使用这个方法有个好处在于,我们可以方便使用 Stream 链式操作,做一些过滤操作。

注意:这里我们使用 try-with-resources 方式,可以安全的确保读取结束,流可以被安全的关闭。

并发读取

逐行的读取的方式,解决我们 OOM 的问题。不过如果数据很多,我们这样一行行处理,需要花费很多时间。

上述的方式,只有一个线程在处理数据,那其实我们可以多来几个线程,增加并行度。

下面在上面的基础上,阿粉就抛砖引玉,介绍下阿粉自己比较常用两种并行处理方式。

逐行批次打包

第一种方式,先逐行读取数据,加载到内存中,等到积累一定数据之后,然后再交给线程池异步处理。

@SneakyThrows public static void readInApacheIOWithThreadPool() {     // 创建一个 最大线程数为 10,队列最大数为 100 的线程池     ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 60l, TimeUnit.SECONDS, new LinkedBlockingDeque<>(100));     // 使用 Apache 的方式逐行读取数据     LineIterator fileContents = FileUtils.lineIterator(new File("temp/test.txt"), StandardCharsets.UTF_8.name());     List<String> lines = Lists.newArrayList();     while (fileContents.hasNext()) {         String nextLine = fileContents.nextLine();         lines.add(nextLine);         // 读取到十万的时候         if (lines.size() == 100000) {             // 拆分成两个 50000 ,交给异步线程处理             List<List<String>> partition = Lists.partition(lines, 50000);             List<Future> futureList = Lists.newArrayList();             for (List<String> strings : partition) {                 Future<?> future = threadPoolExecutor.submit(() -> {                     processTask(strings);                 });                 futureList.add(future);             }             // 等待两个线程将任务执行结束之后,再次读取数据。这样的目的防止,任务过多,加载的数据过多,导致 OOM             for (Future future : futureList) {                 // 等待执行结束                 future.get();             }             // 清除内容             lines.clear();         }      }     // lines 若还有剩余,继续执行结束     if (!lines.isEmpty()) {         // 继续执行         processTask(lines);     }   threadPoolExecutor.shutdown(); }     private static void processTask(List<String> strings) {         for (String line : strings) {             // 模拟业务执行             try {                 TimeUnit.MILLISECONDS.sleep(10L);             } catch (InterruptedException e) {                 e.printStackTrace();             }         }     }

上述方法,等到内存的数据到达 10000 的时候,拆封两个任务交给异步线程执行,每个任务分别处理 50000 行数据。

后续使用 future#get(),等待异步线程执行完成之后,主线程才能继续读取数据。

之所以这么做,主要原因是因为,线程池的任务过多,再次导致 OOM 的问题。

大文件拆分成小文件第二种方式,首先我们将一个大文件拆分成几个小文件,然后使用多个异步线程分别逐行处理数据。

public static void splitFileAndRead() throws Exception {     // 先将大文件拆分成小文件     List<File> fileList = splitLargeFile("temp/test.txt");     // 创建一个 最大线程数为 10,队列最大数为 100 的线程池     ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 60l, TimeUnit.SECONDS, new LinkedBlockingDeque<>(100));     List<Future> futureList = Lists.newArrayList();     for (File file : fileList) {         Future<?> future = threadPoolExecutor.submit(() -> {             try (Stream inputStream = Files.lines(file.toPath(), StandardCharsets.UTF_8)) {                 inputStream.forEach(o -> {                     // 模拟执行业务                     try {                         TimeUnit.MILLISECONDS.sleep(10L);                     } catch (InterruptedException e) {                         e.printStackTrace();                     }                 });             } catch (IOException e) {                 e.printStackTrace();             }         });         futureList.add(future);     }     for (Future future : futureList) {         // 等待所有任务执行结束         future.get();     }     threadPoolExecutor.shutdown();   }  private static List<File> splitLargeFile(String largeFileName) throws IOException {     LineIterator fileContents = FileUtils.lineIterator(new File(largeFileName), StandardCharsets.UTF_8.name());     List<String> lines = Lists.newArrayList();     // 文件序号     int num = 1;     List<File> files = Lists.newArrayList();     while (fileContents.hasNext()) {         String nextLine = fileContents.nextLine();         lines.add(nextLine);         // 每个文件 10w 行数据         if (lines.size() == 100000) {             createSmallFile(lines, num, files);             num++;         }     }     // lines 若还有剩余,继续执行结束     if (!lines.isEmpty()) {         // 继续执行         createSmallFile(lines, num, files);     }     return files; }

上述方法,首先将一个大文件拆分成多个保存 10W 行的数据的小文件,然后再将小文件交给线程池异步处理。

由于这里的异步线程每次都是逐行从小文件的读取数据,所以这种方式不用像上面方法一样担心 OOM 的问题。

另外,上述我们使用 Java 代码,将大文件拆分成小文件。这里阿粉还有一个简单的办法,我们可以直接使用下述命令,直接将大文件拆分成小文件:

# 将大文件拆分成 100000 的小文件  split -l 100000 test.txt

后续 Java 代码只需要直接读取小文件即可。

总结当我们从文件读取数据时,如果文件不是很大,我们可以考虑一次性读取到内存中,然后快速处理。

如果文件过大,我们就没办法一次性加载到内存中,所以我们需要考虑逐行读取,然后处理数据。但是单线程处理数据毕竟有限,所以我们考虑使用多线程,加快处理数据。

感谢各位的阅读,以上就是“怎么用Java高效读取大文件”的内容了,经过本文的学习后,相信大家对怎么用Java高效读取大文件这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: 怎么用Java高效读取大文件

本文链接: https://lsjlt.com/news/84930.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 怎么用Java高效读取大文件
    这篇文章主要讲解了“怎么用Java高效读取大文件”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用Java高效读取大文件”吧!内存读取第一个版本,阿粉采用...
    99+
    2024-04-02
  • Java高效读取大文件实例分析
    1、概述本教程将演示如何用Java高效地读取大文件。Java——回归基础。2、在内存中读取读取文件行的标准方式是在内存中读取,Guava和ApacheCommonsIO都提供了如下所示快速读取文件行的方法:Files.readLines(n...
    99+
    2023-05-30
  • Java如何高效的读取一个超大文件
    这篇文章主要介绍“Java如何高效的读取一个超大文件”,在日常操作中,相信很多人在Java如何高效的读取一个超大文件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java如何...
    99+
    2024-04-02
  • java读取大文件文本怎么处理
    处理大文件文本可以使用Java中的流式处理,以避免一次性将整个文件加载到内存中。以下是一个示例代码,使用BufferedReader...
    99+
    2023-09-20
    java
  • Java中怎么读取文件夹大小
    今天就跟大家聊聊有关Java中怎么读取文件夹大小,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。(一)单线程递归方式package com.taobao.test; ...
    99+
    2023-06-17
  • java怎么通过文件流读取文件大小
    要通过文件流读取文件的大小,可以使用Java中的File类和FileInputStream类来实现。下面是一个示例代码:```jav...
    99+
    2023-09-22
    java
  • PHP怎么读取大文件
    小编给大家分享一下PHP怎么读取大文件,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!衡量成功唯一能确认我们对代码所做改进是否有效的方式是:衡量一个糟糕的情况,然后...
    99+
    2023-06-15
  • python怎么读取大文件
    这篇文章主要介绍“python怎么读取大文件”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“python怎么读取大文件”文章能帮助大家解决问题。可以通过两种方法利用python读取大文件:第一种是利用...
    99+
    2023-06-30
  • java怎么读取文件?
    java中可以使用两个类来读取文件:BufferedReader和BufferedInputStream。1、使用 BufferedReader在下面的实例中,我们将看到两种使用 BufferedReader 来读取文件的方法。在这里, 我...
    99+
    2014-12-22
    java教程 java
  • java如何读取大文件文本
    Java可以使用`BufferedReader`类来读取大文件文本。`BufferedReader`类提供了一个`readLine(...
    99+
    2023-08-08
    java
  • Java中如何使用缓存提高文件读取效率?
    在Java开发中,读取文件是非常常见的操作。但是,如果文件比较大,或者需要频繁读取,那么每次都读取文件的话,效率会非常低下。这时候,我们可以考虑使用缓存来提高文件读取效率。 Java中提供了多种缓存方式,比如使用缓存流、使用Buffere...
    99+
    2023-06-28
    缓存 文件 leetcode
  • 怎么中python读取大文件
    本篇文章为大家展示了怎么中python读取大文件,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。python的五大特点是什么python的五大特点:1.简单易学,开发程序时,专注的是解决问题,而不是搞...
    99+
    2023-06-14
  • java中文件怎么读取
    java的读写操作是学java开发的必经之路,下面我们就来看一下java中文件怎么读。1、使用BufferedReader读取文件BufferedReader是为了提供读的效率而设计的一个包装类,它可以包装字符流。可以从字符输入流中读取文本...
    99+
    2017-06-26
    java
  • java怎么读取xml文件
    在Java中,可以使用Java内置的XML解析器来读取XML文件。下面是一个简单的示例: import javax.xml.pars...
    99+
    2023-10-23
    java
  • java怎么读取zip文件
    要读取zip文件,可以使用Java的java.util.zip包中的ZipInputStream类来实现。下面是一个简单的示例代码,...
    99+
    2023-10-26
    java
  • java怎么读取hadoop文件
    要在Java中读取Hadoop文件,可以使用Hadoop的FileSystem API。以下是一种常见的方法: 首先,创建一个Co...
    99+
    2024-04-02
  • java怎么读取大文件防止内存泄漏
    在Java中,可以使用java.io包中的BufferedReader和FileReader类来读取大文件,同时避免内存泄漏的问题。...
    99+
    2023-09-12
    java
  • java读取大文件内存溢出怎么解决
    当Java读取大文件时,可能会遇到内存溢出的问题。这是因为默认情况下,Java在读取文件时会将文件的所有内容一次性加载到内存中,如果...
    99+
    2023-10-23
    java
  • java读取大文件怎么防止内存泄露
    在Java中,可以使用以下方法来防止内存泄露:1. 使用缓冲读取:使用BufferedReader或者Scanner类来读取大文件,...
    99+
    2023-08-24
    java
  • Python 读取大文件
    在处理大数据时,有可能会碰到好几个 G 大小的文件。如果通过一些工具(例如:NotePad++)打开它,会发生错误,无法读取任何内容。 那么,在 Python 中,如何快速地读取这些大文件呢? | 版权声明:一去、二三里,未经博...
    99+
    2023-01-31
    大文件 Python
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作