Java 大文件IO操作效率对比【我说说 你瞅瞅】
Java 文件IO操作效率对比
注:本文只做时间消耗层面对比,内存占用层面需要特别关注!
1. 参数说明
文件总大小:2,111,993,850 字节(2.11 GB)
static String defaultFilePath = "/tmp/data-24081412.json";
缓冲区大小:8192
static int defaultByteLength = 1024 * 8;
2. 示例介绍
通过以下几种方式读取数据文件,并连续进行 10 次测试:
- FileInputStream + byte[] 文件字节输入流 + 字节数组读取方式
- FileInputStream + Scanner 文件字节输入流 + Scanner 读取方式
- FileReader + char[] 文件字符输入流 + 字符数组方式
- BufferedReader 缓冲字符流方式
- FileChannel 文件输入输出管道流「NIO」
3. 比对结果
「5. FileChannel」 > 「1. FileInputStream + byte[]」> 「3. FileReader + char[]」>= 「4. BufferedReader」 > 「2. FileInputStream + Scanner」
在操作文件时,会将文件区分为大文件、小文件、文本文件、二进制文件等等吗,根据不同的文件需要选择合适的读取方式。通常大文件推荐使用 「FileChannel」效率会更高,小文件采用 IO 或 NIO 都可以。
4. 示例代码
4.1. FileInputStream + byte[] 文件字节输入流 + 字节数组读取方式
/**
* FileInputStream + byte[] 方式
* 等同于 BufferedInputStream 字节输入缓冲流
* 文件字节输入流 + 字节数组读取方式
* 适用于:二进制文件或非文本文件
*/
public void testFileInputStreamWithBytes() {
long startTime = new Date().getTime();
// 使用 try 包装
try (FileInputStream fileInputStream = new FileInputStream(defaultFilePath)){
byte[] reads = new byte[defaultByteLength];
int readCount;
while ((readCount = fileInputStream.read(reads)) != -1) {
// TODO 处理数据
}
} catch (IOException e) {
System.out.printf("读取文件异常[ %s ]%n", e.getMessage());
}
System.out.printf("FileInputStream + byte[] 方式 读取文件共使用 %d 毫秒%n", new Date().getTime() - startTime);
}
10 次测试结果如下
FileInputStream + byte[] 方式 读取文件共使用 884 毫秒
FileInputStream + byte[] 方式 读取文件共使用 331 毫秒
FileInputStream + byte[] 方式 读取文件共使用 319 毫秒
FileInputStream + byte[] 方式 读取文件共使用 420 毫秒
FileInputStream + byte[] 方式 读取文件共使用 333 毫秒
FileInputStream + byte[] 方式 读取文件共使用 321 毫秒
FileInputStream + byte[] 方式 读取文件共使用 327 毫秒
FileInputStream + byte[] 方式 读取文件共使用 339 毫秒
FileInputStream + byte[] 方式 读取文件共使用 328 毫秒
FileInputStream + byte[] 方式 读取文件共使用 398 毫秒
4.2. FileInputStream + Scanner 文件字节输入流 + Scanner 读取方式
/**
* FileInputStream + Scanner 方式
* 文件字节输入流 + Scanner 读取文本方式
* 适用于:文本文件
*/
public void testFileInputStreamWithScanner() {
long startTime = new Date().getTime();
// 使用 try 包装
try (FileInputStream fileInputStream = new FileInputStream(defaultFilePath)){
Scanner scanner = new Scanner(fileInputStream);
while (scanner.hasNext()) {
scanner.nextLine();
// TODO 处理数据
}
} catch (IOException e) {
System.out.printf("读取文件异常[ %s ]%n", e.getMessage());
}
System.out.printf("FileInputStream + Scanner 方式 读取文件共使用 %d 毫秒%n", new Date().getTime() - startTime);
}
10 次测试结果如下
没有缓冲区,性能急剧下降!!
FileInputStream + Scanner 方式 读取文件共使用 16755 毫秒
FileInputStream + Scanner 方式 读取文件共使用 18744 毫秒
FileInputStream + Scanner 方式 读取文件共使用 17929 毫秒
FileInputStream + Scanner 方式 读取文件共使用 18640 毫秒
FileInputStream + Scanner 方式 读取文件共使用 18316 毫秒
FileInputStream + Scanner 方式 读取文件共使用 18015 毫秒
FileInputStream + Scanner 方式 读取文件共使用 18479 毫秒
FileInputStream + Scanner 方式 读取文件共使用 18755 毫秒
FileInputStream + Scanner 方式 读取文件共使用 18907 毫秒
FileInputStream + Scanner 方式 读取文件共使用 18783 毫秒
4.3. FileReader + char[] 文件字符输入流 + 字符数组方式
/**
* FileReader + char[] 方式
* 等同于 BufferedReader 字符输入缓冲流
* 文件字符输入流 + 字符数组方式
* 适用于:字符文件
*/
public void testFileReaderWithChars() {
long startTime = new Date().getTime();
// 使用 try 包装
try (FileReader fileReader = new FileReader(defaultFilePath)){
char[] reads = new char[defaultByteLength];
int readCount;
while ((readCount = fileReader.read(reads)) != -1) {
// TODO 处理数据
}
} catch (IOException e) {
System.out.printf("读取文件异常[ %s ]%n", e.getMessage());
}
System.out.printf("FileReader + char[] 方式 读取文件共使用 %d 毫秒%n", new Date().getTime() - startTime);
}
10 次测试结果如下
FileReader + char[] 方式 读取文件共使用 922 毫秒
FileReader + char[] 方式 读取文件共使用 971 毫秒
FileReader + char[] 方式 读取文件共使用 842 毫秒
FileReader + char[] 方式 读取文件共使用 985 毫秒
FileReader + char[] 方式 读取文件共使用 868 毫秒
FileReader + char[] 方式 读取文件共使用 1207 毫秒
FileReader + char[] 方式 读取文件共使用 1031 毫秒
FileReader + char[] 方式 读取文件共使用 981 毫秒
FileReader + char[] 方式 读取文件共使用 1259 毫秒
FileReader + char[] 方式 读取文件共使用 1034 毫秒
4.4. BufferedReader 缓冲字符流方式
/**
* BufferedReader 方式
* 缓冲字符流方式
* 适用于:字符文件
*/
public void testBufferedReader() {
long startTime = new Date().getTime();
// 使用 try 包装
try (BufferedReader fileReader = new BufferedReader(new FileReader(defaultFilePath))){
String line;
while ((line = fileReader.readLine()) != null) {
// TODO 处理数据
}
} catch (IOException e) {
System.out.printf("读取文件异常[ %s ]%n", e.getMessage());
}
System.out.printf("BufferedReader 方式 读取文件共使用 %d 毫秒%n", new Date().getTime() - startTime);
}
10 次测试结果如下
BufferedReader 方式 读取文件共使用 1870 毫秒
BufferedReader 方式 读取文件共使用 1895 毫秒
BufferedReader 方式 读取文件共使用 1890 毫秒
BufferedReader 方式 读取文件共使用 1875 毫秒
BufferedReader 方式 读取文件共使用 1829 毫秒
BufferedReader 方式 读取文件共使用 2060 毫秒
BufferedReader 方式 读取文件共使用 1821 毫秒
BufferedReader 方式 读取文件共使用 1944 毫秒
BufferedReader 方式 读取文件共使用 1902 毫秒
BufferedReader 方式 读取文件共使用 1860 毫秒
4.5. FileChannel 文件输入输出管道流「NIO」
/**
* FileChannel 方式
* 文件输入输出管道流
*/
public void testFileChannel() {
long startTime = new Date().getTime();
// 使用 try 包装
try (FileChannel channel = FileChannel.open(Paths.get(defaultFilePath), StandardOpenOption.READ)){
// 构造B yteBuffer 有两个方法,ByteBuffer.allocate和 ByteBuffer.allocateDirect,两个方法都是相同入参,含义却不同。
// ByteBuffer.allocate(capacity) 分配的是非直接缓冲区,非直接缓冲区的操作会在Java堆内存中进行,数据的读写会通过Java堆内存来传递。
// ByteBuffer.allocateDirect(capacity) 分配的是直接缓冲区, 直接缓冲区的操作可以通过本地I/O传递,避免了在Java堆和本地堆之间的数据传输。
ByteBuffer buf = ByteBuffer.allocate(defaultByteLength);
while (channel.read(buf) != -1) {
buf.flip();
// TODO 处理数据
// System.out.println(new String(buf.array()));
buf.clear();
}
} catch (IOException e) {
System.out.printf("读取文件异常[ %s ]%n", e.getMessage());
}
System.out.printf("FileChannel 方式 读取文件共使用 %d 毫秒%n", new Date().getTime() - startTime);
}
10 次测试结果如下
FileChannel 方式 读取文件共使用 314 毫秒
FileChannel 方式 读取文件共使用 293 毫秒
FileChannel 方式 读取文件共使用 332 毫秒
FileChannel 方式 读取文件共使用 296 毫秒
FileChannel 方式 读取文件共使用 285 毫秒
FileChannel 方式 读取文件共使用 290 毫秒
FileChannel 方式 读取文件共使用 283 毫秒
FileChannel 方式 读取文件共使用 282 毫秒
FileChannel 方式 读取文件共使用 298 毫秒
FileChannel 方式 读取文件共使用 280 毫秒
5. 结语
在 Java 8 中,「FileChannel」是处理文件 I/O 的高效方式,相比于传统的 I/O流「FileInputStream」、「FileOutputStream」等更加灵活方便且效率更高。
6. 代码附录
package com.demo.io;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Date;
import java.util.Scanner;
/**
* 文件读取
* @date 2024-08-14 16:42:21
*/
public class FileReadTest {
// 文件路径
static String defaultFilePath = "/Users/changbeibei/Desktop/work/wfilep.log-24081412";
// 8k
static int defaultByteLength = 1024 * 8;
/**
* FileInputStream + byte[] 方式
* 等同于 BufferedInputStream 字节输入缓冲流
* 文件字节输入流 + 字节数组读取方式
* 适用于:二进制文件或非文本文件
*/
@Test
public void testFileInputStreamWithBytes() {
long startTime = new Date().getTime();
// 使用 try 包装
try (FileInputStream fileInputStream = new FileInputStream(defaultFilePath)){
byte[] reads = new byte[defaultByteLength];
int readCount;
while ((readCount = fileInputStream.read(reads)) != -1) {
// TODO 处理数据
}
} catch (IOException e) {
System.out.printf("读取文件异常[ %s ]%n", e.getMessage());
}
System.out.printf("FileInputStream + byte[] 方式 读取文件共使用 %d 毫秒%n", new Date().getTime() - startTime);
}
/**
* FileInputStream + Scanner 方式
* 文件字节输入流 + Scanner 读取文本方式
* 适用于:文本文件
*/
@Test
public void testFileInputStreamWithScanner() {
long startTime = new Date().getTime();
// 使用 try 包装
try (FileInputStream fileInputStream = new FileInputStream(defaultFilePath)){
Scanner scanner = new Scanner(fileInputStream);
while (scanner.hasNext()) {
scanner.nextLine();
// TODO 处理数据
}
} catch (IOException e) {
System.out.printf("读取文件异常[ %s ]%n", e.getMessage());
}
System.out.printf("FileInputStream + Scanner 方式 读取文件共使用 %d 毫秒%n", new Date().getTime() - startTime);
}
/**
* FileReader + char[] 方式
* 等同于 BufferedReader 字符输入缓冲流
* 文件字符输入流 + 字符数组方式
* 适用于:字符文件
*/
@Test
public void testFileReaderWithChars() {
long startTime = new Date().getTime();
// 使用 try 包装
try (FileReader fileReader = new FileReader(defaultFilePath)){
char[] reads = new char[defaultByteLength];
int readCount;
while ((readCount = fileReader.read(reads)) != -1) {
// TODO 处理数据
}
} catch (IOException e) {
System.out.printf("读取文件异常[ %s ]%n", e.getMessage());
}
System.out.printf("FileReader + char[] 方式 读取文件共使用 %d 毫秒%n", new Date().getTime() - startTime);
}
/**
* BufferedReader 方式
* 缓冲字符流方式
* 适用于:字符文件
*/
@Test
public void testBufferedReader() {
long startTime = new Date().getTime();
// 使用 try 包装
try (BufferedReader fileReader = new BufferedReader(new FileReader(defaultFilePath))){
String line;
while ((line = fileReader.readLine()) != null) {
// TODO 处理数据
}
} catch (IOException e) {
System.out.printf("读取文件异常[ %s ]%n", e.getMessage());
}
System.out.printf("BufferedReader 方式 读取文件共使用 %d 毫秒%n", new Date().getTime() - startTime);
}
/**
* FileChannel 方式
* 文件输入输出管道流
*/
@Test
public void testFileChannel() {
long startTime = new Date().getTime();
// 使用 try 包装
try (FileChannel channel = FileChannel.open(Paths.get(defaultFilePath), StandardOpenOption.READ)){
// 构造B yteBuffer 有两个方法,ByteBuffer.allocate和 ByteBuffer.allocateDirect,两个方法都是相同入参,含义却不同。
// ByteBuffer.allocate(capacity) 分配的是非直接缓冲区,非直接缓冲区的操作会在Java堆内存中进行,数据的读写会通过Java堆内存来传递。
// ByteBuffer.allocateDirect(capacity) 分配的是直接缓冲区, 直接缓冲区的操作可以通过本地I/O传递,避免了在Java堆和本地堆之间的数据传输。
ByteBuffer buf = ByteBuffer.allocate(defaultByteLength);
while (channel.read(buf) != -1) {
buf.flip();
// TODO 处理数据
// System.out.println(new String(buf.array()));
buf.clear();
}
} catch (IOException e) {
System.out.printf("读取文件异常[ %s ]%n", e.getMessage());
}
System.out.printf("FileChannel 方式 读取文件共使用 %d 毫秒%n", new Date().getTime() - startTime);
}
@Test
public void testMain() {
for (int i = 0; i < 10; i++) {
System.out.printf("第 %d 次测试%n", i + 1);
testFileInputStreamWithBytes();
testFileInputStreamWithScanner();
testFileReaderWithChars();
testBufferedReader();
testFileChannel();
}
}
}
Java 大文件IO操作效率对比【我说说 你瞅瞅】的更多相关文章
- Java:大文件拆分工具
java大文件拆分工具(过滤掉表头) import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File ...
- java 大文件上传 断点续传 完整版实例 (Socket、IO流)
ava两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下: 实现思路: 1.服:利用ServerSocket搭建服务器,开启相应端口,进行长连接操 ...
- java 大文件分割与组装
不多说,直接上代码 1 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; im ...
- java大文件断点续传
java两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下: 实现思路:1.服:利用ServerSocket搭建服务器,开启相应端口,进行长连接操 ...
- java大文件读写操作,java nio 之MappedByteBuffer,高效文件/内存映射
java处理大文件,一般用BufferedReader,BufferedInputStream这类带缓冲的Io类,不过如果文件超大的话,更快的方式是采用MappedByteBuffer. Mapped ...
- Java 之文件IO编程 之写入
package com.sun; /* * 操作对文件IO的写 * 2014-08-10 */ import java.io.*; public class File_Write { public s ...
- Java 之文件IO编程 之读取
package com.sun; /* * 这里是对文件IO流读取的操作 * 2014-08-10 */ import java.io.*; public class File_test { publ ...
- java 导出 excel 最佳实践,java 大文件 excel 避免OOM(内存溢出) excel 工具框架
产品需求 产品经理需要导出一个页面的所有的信息到 EXCEL 文件. 需求分析 对于 excel 导出,是一个很常见的需求. 最常见的解决方案就是使用 poi 直接同步导出一个 excel 文件. 客 ...
- java 流 文件 IO
Java 流(Stream).文件(File)和IO Java.io 包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io 包中的流支持很多种格式,比如:基本类 ...
- java大文件上传解决方案
最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表 ...
随机推荐
- 支付宝 返回的form如何在前端打开
支付宝支付时返回了一段标签标签大概是 <form></form><script></script> 试了innerHtml怎么试都不能用,是那种直接把字 ...
- Shell读取整行
像C/C++,JAVA,Python等语言中,可以轻松地对文件进行按行读取. 那么,Shell中怎么实现对行读取呢? #!/bin/bash while read i do echo $i done ...
- FEDORA34 安装CUDA11.3
FEDORA34 安装CUDA11.3 首先确保能装上最新的显卡驱动. FEDORA显卡驱动安装 然后去官网下fedora对应的.run安装文件.运行安装, 但不要选装驱动. 完成后加环境变量: # ...
- .NET 认识日志系统-2
.NET 日志系统2 上一篇文章是将日志打印到控制台,这篇文章将日志写入到文本文件中. 文本日志一般按照日期区分 如何避免文本日志把磁盘撑爆? 限制日志总个数或者总大小 如何避免一个日志文件太大? 限 ...
- 高通平台UEFI有关介绍
高通平台UEFI有关介绍 背景 我需要在高通平台上学习点亮LCD,目前通过同事在别的平台的配置代码,我已经将kernel部分的屏幕点亮了:剩余的工作量就在BP侧,也就是系统刚开机的那一段时间.在开发过 ...
- B 站和小红书又又又崩了,罪魁祸首竟然又是他。。。
大家好,我是凌晨. 今天上午10点左右,我打开B站发现无法刷新视频列表和评论区,收藏夹和弹幕也均不可用. 原以为是手机网络问题,换网络重启手机都还是不行,第一时间打开微博,果然,B站崩了的新闻荣登榜首 ...
- jsp---------------------复选框,全选按钮
js部分:注意:<script type="text/javascript" ></script>内不能有其他内容,否则会无效,若有则另起一对<scr ...
- windows中运行nicegui官网内容
内容受到bilibili视频nicegui官网访问不了?教你本地部署文档,我写python终于有界面启发,其余部分由chatgpt协助生成. 1. 在本地新建目录如 nicegui_web 2. 从g ...
- Linux Centos7搭建RabbitMQ
下载依赖 yum -y install epel-release yum -y update 安装Erlang yum -y install erlang socat 测试安装成功 erl -vers ...
- 最简GIF解析代码gif_jumper,用于stb_image的小改进
gif jumper gif支持多帧动画,但是没有存储总帧数,解析gif直到结束才能知道总帧数. 所以gif解析代码,要么采用链表,要么不停realloc()分配内存,stb_image的代码就是如此 ...