Java 大数据量输入输出优化方案详解:从 Scanner 到手写快读(含漫画解析)

Java 大数据量输入输出优化方案详解:从 Scanner 到手写快读(其中方案全部使用计数排序)

前言

在算法竞赛或处理大规模数据的工程场景中,Java 程序常因输入输出(IO)效率低下而超时。尤其当数据量达到数十万甚至数百万级别时,使用默认的 ScannerSystem.out 往往无法满足性能要求。本文将介绍几种 Java 中高效处理大量整数输入输出的方法,并结合具体的题目来给出真实的测试

问题背景

该题目为洛谷的P1271 【深基9.例1】选举学生会,是一道典型的排序加大数据量读写的题目,题目看似不难,但使用java语言进行做题时,如若使用传统Scannersout读写将导致判题不通过,因此要考虑使用高效的io或手写快读方式进行读写


方案一:基础方法 ------ Scanner(不推荐)

java 复制代码
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
// ...

漫画解析:


缺点分析:

  • Scanner 内部使用正则表达式解析 token,开销大;
  • 每次调用 nextInt() 都涉及字符串创建与解析;
  • 未使用高效缓冲机制,频繁系统调用导致性能瓶颈;
  • 在 2e6 数据量下,运行时间通常超过 1500ms,极易超时。

测评结果如下:

可以很明显的看到在进行大数据量的读取时,空间复杂度直接不通过本题

结论:仅适用于教学或小规模数据,竞赛中应避免使用。


方案二:BufferedReader + StringTokenizer(常用方案)

java 复制代码
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());

漫画解析:


优势:

  • BufferedReader 提供字符流缓冲,大幅减少系统调用次数;
  • StringTokenizer 快速分割字符串,无正则开销;
  • 配合 Integer.parseInt() 解析整数,效率显著优于 Scanner

局限性:

  • 仍需将子串(如 "123")转换为 String 对象,再解析为整数;
  • 每次 parseInt 涉及字符遍历与数值计算,存在冗余内存分配;
  • 在极限数据下仍有优化空间。

适用场景: 大多数 OJ 平台的标准解法,稳定且易于理解。

测评结果如下:

可以看到相比于方案一,时间复杂度明显降低,空间复杂度也降了下来


方案三:BufferedReader + StreamTokenizer(更优方案)

java 复制代码
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();
int n = (int) st.nval;

漫画解析:

核心优势:

  • StreamTokenizer 在读取过程中直接解析数值,无需中间字符串;
  • 数值结果直接存入 nval(double 类型),避免 parseInt 开销;
  • 自动跳过空白字符(空格、换行等),兼容多行或单行输入;
  • 内存分配极少,GC 压力小。

注意事项:

  • nvaldouble 类型,需强制转为 int,但在题目保证整数范围内安全;
  • 默认支持负数,通用性强。

性能表现: 在 2e6 整数输入下,通常耗时 400~600ms,远优于前两种方法。

适用场景: 需要高吞吐整数读取的竞赛题,代码简洁且高效。

测评结果如下:

可以看到相比于方案二,时间复杂度和空间复杂度进一步的降低


方案四:手写快读 + PrintWriter(极致优化)

java 复制代码
static int readInt() throws IOException {
    int x = 0, c;
    boolean neg = false;
    while ((c = System.in.read()) <= ' ');
    if (c == '-') { neg = true; c = System.in.read(); }
    while (c >= '0' && c <= '9') {
        x = x * 10 + (c - '0');
        c = System.in.read();
    }
    return neg ? -x : x;
}

漫画解析:


优化原理:

  • 直接调用 System.in.read() 读取字节,绕过字符流封装;
  • 手动解析数字,无任何对象创建(零 String、零 Token);
  • 跳过所有空白字符,自动处理换行与空格;
  • 输出使用 PrintWriter 包裹 BufferedOutputStream,确保写入缓冲。

性能优势:

  • StreamTokenizer 再快 20%~30%;
  • 在 2e6 数据下可控制在 300~500ms 内;
  • 极低内存占用,适合内存敏感环境。

适用场景: 数据量极大(≥1e6)、时限紧张的压线题目;追求极致性能的选手首选。

测评结果如下:

提交测评后可以看到虽然相比方案三耗时相差无几,但是明显更加节省空间


输出优化:统一使用 PrintWriter

无论采用哪种输入方式,输出部分都应避免直接使用 System.out.print。原因如下:

  • System.out 是未缓冲的 PrintStream,每次调用都可能触发系统写操作;
  • 使用 PrintWriter 包裹 BufferedOutputStream 可将输出缓存,最后一次性刷新。

推荐写法:

java 复制代码
// 创建 PrintWriter 包装 BufferedOutputStream,提升输出效率
// - System.out 是字节流,直接打印频繁时效率低
// - BufferedOutputStream 提供缓冲区,减少系统调用次数
// - PrintWriter 提供方便的 print/println 方法,并支持自动刷新(可选)
PrintWriter pw = new PrintWriter(new BufferedOutputStream(System.out));

// 输出一个整数 x(或其他基本类型)
// pw.print(x) 会将 x 转为字符串并写入缓冲区
pw.print(x);

// 输出一个空格:使用 write(' ') 比 print(' ') 更轻量
// - write(char) 直接写入单个字符(实际是写入其低 8 位字节)
// - print(char) 会经过更多方法调用和内部检查,开销略大
// 注意:write(int) 实际写的是字节,所以 ' '(ASCII 32)能正确输出空格
pw.write(' ');

// 强制将缓冲区内容刷出到控制台
// - 若不 flush,程序结束前可能看不到输出(尤其在 OJ 中可能导致 WA)
// - 通常在所有输出完成后 flush 一次即可,无需每次 print 都 flush
pw.flush();

// 关闭 PrintWriter(也会自动 flush 并关闭底层流)
// - 良好习惯:释放资源
// - 注意:关闭后不能再使用 pw,否则抛异常
pw.close();

漫画解析:


总结与建议

方法 时间复杂度(IO) 代码复杂度 推荐指数 适用场景
Scanner ★☆☆☆☆ 小数据、学习
BufferedReader + StringTokenizer ★★★★☆ 一般竞赛题
StreamTokenizer ★★★★★ 大整数输入,推荐默认使用
手写快读 极低 ★★★★★ 极限性能需求

实践建议:

  1. 日常练习可使用 StreamTokenizer,兼顾效率与简洁;
  2. 遇到 1e6+ 数据且时限紧张时,启用手写快读;
  3. 输出务必使用 PrintWriter + BufferedOutputStream
  4. 不必过度纠结尾部空格问题,主流 OJ 均允许。

通过合理选择 IO 策略,即使是 Java 这类"慢语言",也能在大数据场景下高效运行。掌握这些技巧,将显著提升你在算法竞赛和工程实践中的竞争力。

相关推荐
tb_first2 小时前
SSM速通3
java·jvm·spring boot·mybatis
weixin_395448912 小时前
main.c_cursor_0202
前端·网络·算法
一起养小猫2 小时前
Flutter for OpenHarmony 实战:番茄钟应用完整开发指南
开发语言·jvm·数据库·flutter·信息可视化·harmonyos
独自破碎E2 小时前
总持续时间可被 60 整除的歌曲
java·开发语言
Python+JAVA+大数据2 小时前
TCP_IP协议栈深度解析
java·网络·python·网络协议·tcp/ip·计算机网络·三次握手
丶小鱼丶2 小时前
Java基础之【多线程】
java
senijusene2 小时前
数据结构与算法:队列与树形结构详细总结
开发语言·数据结构·算法
杜家老五2 小时前
综合实力与专业服务深度解析 2026北京网站制作公司六大优选
数据结构·算法·线性回归·启发式算法·模拟退火算法
好好沉淀2 小时前
Elasticsearch 中获取返回匹配记录总数
开发语言·elasticsearch