如何使用 Java 高效读取大文件
文章目录1.介绍2.读在内存中3.通过文件流读取4.使用 Apache Commons IO 进行流传输1.介绍如何以高效的方式从 Java 大文件中读取所有行。2.读在内存中准备准备1G作用的数据@Testpublic void test0() throws IOException {final String json = "[{\"id\":\"380001671739949056\",\"t
1.介绍
如何以高效的方式从 Java 大文件中读取所有行。
2.读在内存中
准备
准备1G作用的数据
@Test
public void test0() throws IOException {
final String json = "[{\"id\":\"380001671739949056\",\"type\":\"3\",\"work_no\":\"chn001\",\"display_name\":\"测试chn\",\"display_py\":\"ceschn\",\"country\":\"1001\",\"id_card_no\":null,\"birthday\":null,\"sex\":null,\"secret_level\":\"1\",\"nationality\":\"1\",\"mobile\":null,\"sw_email\":null,\"ip_phone\":null,\"leave_date\":null,\"marital_status\":null,\"certificate_cn\":null,\"politics_status\":null,\"native_place\":null,\"work_time\":null,\"image\":null,\"status\":1,\"update_time\":1631601059000,\"create_time\":1631601076000,\"userJobInfo\":[{\"company_id\":\"129343111846100992\",\"company_job_type\":\"0\",\"jh_email\":null,\"email\":null,\"office_address\":null,\"job_level\":null,\"depJobInfo\":[{\"org_id\":\"129343111846100992\",\"dept_job_type\":\"1\",\"job\":null,\"job_status\":null,\"position\":null,\"show_order\":null,\"valid_time\":null,\"job_attribute\":null}]}],\"syncStatus\":2000,\"syncSequence\":380001673615736833},{\"id\":\"380388589673725952\",\"type\":\"1\",\"work_no\":\"727411\",\"display_name\":\"冬测试导入用户-11\",\"display_py\":\"dongcsdryh-11\",\"country\":null,\"id_card_no\":null,\"birthday\":null,\"sex\":null,\"secret_level\":\"1\",\"nationality\":null,\"mobile\":null,\"sw_email\":null,\"ip_phone\":null,\"leave_date\":null,\"marital_status\":null,\"certificate_cn\":null,\"politics_status\":null,\"native_place\":null,\"work_time\":null,\"image\":null,\"status\":1,\"update_time\":1631693308000,\"create_time\":1631693324000,\"userJobInfo\":[{\"company_id\":\"114836879844122624\",\"company_job_type\":\"0\",\"jh_email\":null,\"email\":null,\"office_address\":null,\"job_level\":null,\"depJobInfo\":[{\"org_id\":\"124944489058598912\",\"dept_job_type\":\"1\",\"job\":null,\"job_status\":null,\"position\":null,\"show_order\":null,\"valid_time\":null,\"job_attribute\":null}]}],\"syncStatus\":2000,\"syncSequence\":380388591876669441}]";
final BufferedWriter writer = new BufferedWriter(new FileWriter("D:\\test.txt"));
long start = System.currentTimeMillis();
IntStream.range(0, 800000).forEach(value -> {
try {
//新起一行 写数据
writer.newLine();
writer.write(json);
} catch (IOException e) {
e.printStackTrace();
}
});
long end = System.currentTimeMillis();
writer.close();
System.out.println((end - start) / 1000 + "ms");
//1.23g 写完 需要7s
}
读取文件行的标准方法是在内存中——Guava 和 Apache Commons IO 都提供了一种快速的方法来做到这一点:
Guava
@Test
public void test1() throws IOException {
Files.readLines(new File(path), Charsets.UTF_8);
}
Apache
@Test
public void test2() throws IOException {
FileUtils.readLines(new File(path));
}
这种方法的问题是所有文件行都保存在内存中——如果文件足够大,这将很快导致 OutOfMemoryError。
例如——读取一个 ~1Gb 的文件:
private void logMemory() {
System.out.println("最大内存: " + Runtime.getRuntime()
.maxMemory() / 1048576 + "Mb");
System.out.println("总内存:" + Runtime.getRuntime()
.totalMemory() / 1048576+"Mb");
System.out.println("空闲内存:"+ + Runtime.getRuntime()
.freeMemory() / 1048576+ "Mb");
}
@Test
public void test1() throws IOException {
final String path = "D:\\test.txt";
logMemory();
Files.readLines(new File(path), Charsets.UTF_8);
logMemory();
}
这从消耗少量内存开始:(消耗约 0 Mb)
[main] INFO com.demo.javabase.ReadFileTest - 总内存: 128 Mb
[main] INFO com.demo.javabase.ReadFileTest - 空闲内存: 116 Mb
但是,在处理完完整文件后,最终得到了:(消耗了约 2 Gb)
[main] INFO com.demo.javabase.ReadFileTest - 总内存: 2666 Mb
[main] INFO com.demo.javabase.ReadFileTest - 空闲内存: 490 Mb
这意味着进程消耗了大约 2.1 Gb 的内存——原因很简单——文件的行现在都存储在内存中。
很明显,将文件内容保存在内存中会很快耗尽可用内存——不管实际有多少。
更重要的是,通常不需要一次性将文件中的所有行都保存在内存中——相反,只需要能够遍历每一行,进行一些处理并将其丢弃。 所以,要做的就是——遍历行而不将所有行都保存在内存中。
3.通过文件流读取
一个解决方案——使用 java.util.Scanner 来遍历文件的内容并逐行检索行:
@Test
public void test3() throws Exception {
logMemory();
final String path = "D:\\test.txt";
FileInputStream inputStream = null;
Scanner sc = null;
try {
inputStream = new FileInputStream(path);
sc = new Scanner(inputStream, "UTF-8");
//一行一行读取数据
while (sc.hasNextLine()) {
String line = sc.nextLine();
System.out.println(line);
}
if (sc.ioException() != null) {
throw sc.ioException();
}
} finally {
if (inputStream != null) {
inputStream.close();
}
if (sc != null) {
sc.close();
}
}
logMemory();
}
该解决方案将遍历文件中的所有行 - 允许处理每一行 - 不保留对它们的引用 - 总之,不将它们保留在内存中:(消耗约 150 Mb)
[main] INFO com.demo.javabase.ReadFileTest - 总内存: 763 Mb
[main] INFO com.demo.javabase.ReadFileTest - 空闲内存: 605 Mb
4.使用 Apache Commons IO 进行流传输
使用 Commons IO 库也可以通过使用库提供的自定义 LineIterator 来实现相同的目的:
由于整个文件并未完全在内存中 - 这也会导致相当保守的内存消耗数字:(消耗约 150 Mb)
@Test
public final void test6() throws IOException {
final String path = "D:\\test.txt";
logMemory();
final LineIterator it = FileUtils.lineIterator(new File(path), "UTF-8");
try {
while (it.hasNext()) {
final String line = it.nextLine();
System.out.println(line);
}
} finally {
LineIterator.closeQuietly(it);
}
logMemory();
}
由于整个文件并未完全在内存中 - 这也会导致相当保守的内存消耗数字:(消耗约 150 Mb)
[main] INFO com.demo.javabase.ReadFileTest - 总内存: 752 Mb
[main] INFO com.demo.javabase.ReadFileTest - 空闲内存: 564 Mb
更多推荐
所有评论(0)