从 Java 中读取 System.in 的最快方法是啥?
Posted
技术标签:
【中文标题】从 Java 中读取 System.in 的最快方法是啥?【英文标题】:What's the fastest way to read from System.in in Java?从 Java 中读取 System.in 的最快方法是什么? 【发布时间】:2011-10-26 07:32:44 【问题描述】:我在使用Scanner(System.in)
时从标准中读取由空格或换行符分隔的一堆整数。
在 Java 中有没有更快的方法来做到这一点?
【问题讨论】:
Read large amount of data from file in Java的可能重复 每秒需要读入多少百万个整数?如果你有不到几百万,我不会太担心。 我在编程比赛中遇到了这个问题。您会得到数千个问题实例,每个实例都有数千个数字,这并不罕见(以确保您不会逃脱复杂性较差的解决方案)。 是的,这是针对我正在阅读数千行代码的编程竞赛,我注意到 C++ 中的 cin 比 Java 中的 Scanner 快得多,并且想知道是否存在替代方案。跨度> 未来的搜索者也应该看看这个帖子:***.com/questions/691184/… 【参考方案1】:在 Java 中有更快的方法吗?
是的。扫描仪相当慢(至少根据我的经验)。
如果您不需要验证输入,我建议您将流包装在 BufferedInputStream 中并使用类似String.split
/ Integer.parseInt
的东西。
一个小比较:
使用此代码读取 17 兆字节(4233600 个数字)
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext())
sum += scanner.nextInt();
占用我的机器 3.3 秒。而这个sn-p
BufferedReader bi = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = bi.readLine()) != null)
for (String numStr: line.split("\\s"))
sum += Integer.parseInt(numStr);
花了 0.7 秒。
通过进一步弄乱代码(使用String.indexOf
/ String.substring
迭代line
),您可以很容易地将其缩短到大约0.1 秒,但我想我已经回答了您的问题,但我没有想把它变成一些代码高尔夫。
【讨论】:
不过,您最好有充分的理由将这种混乱添加到您的代码中。 String.split 不如 StringTokenizer 快。要获得最有效的代码,请使用 StringTokenizer【参考方案2】:我创建了一个小的 InputReader 类,它的工作原理与 Java 的 Scanner 类似,但在速度上优于它很多数量级,事实上,它也优于 BufferedReader。这是一个条形图,显示了我创建的 InputReader 类从标准输入读取不同类型数据的性能:
以下是使用 InputReader 类查找来自 System.in 的所有数字之和的两种不同方法:
int sum = 0;
InputReader in = new InputReader(System.in);
// Approach #1
try
// Read all strings and then parse them to integers (this is much slower than the next method).
String strNum = null;
while( (strNum = in.nextString()) != null )
sum += Integer.parseInt(strNum);
catch (IOException e)
// Approach #2
try
// Read all the integers in the stream and stop once an IOException is thrown
while( true ) sum += in.nextInt();
catch (IOException e)
【讨论】:
不幸的是,InputReader.readLine()
使用 OpenJKD8 并没有明显快于 BufferedReader.readLine()
。缓冲区大小为 2048,流长度为 2.5 MB。但更糟糕的是:代码中断 (UTF-8) 字符编码。【参考方案3】:
如果你从竞争编程的角度来问,如果提交速度不够快,那就是 TLE。 然后您可以检查以下方法以从 System.in 中检索字符串。 我取自 java 中最好的编码器之一(竞争网站)
private String ns()
int b = skip();
StringBuilder sb = new StringBuilder();
while(!(isSpaceChar(b))) // when nextLine, (isSpaceChar(b) && b != ' ')
sb.appendCodePoint(b);
b = readByte();
return sb.toString();
`
【讨论】:
【参考方案4】:您可以逐位读取System.in
。看看这个答案:https://***.com/a/2698772/3307066。
我在这里复制代码(几乎没有修改)。基本上,它读取整数,由任何非数字分隔。 (感谢原作者。)
private static int readInt() throws IOException
int ret = 0;
boolean dig = false;
for (int c = 0; (c = System.in.read()) != -1; )
if (c >= '0' && c <= '9')
dig = true;
ret = ret * 10 + c - '0';
else if (dig) break;
return ret;
在我的问题中,这段代码大约是。比使用 StringTokenizer
快 2 倍,后者已经比 String.split(" ")
快。
(问题涉及读取 100 万个整数,每个整数最多 100 万个。)
【讨论】:
【参考方案5】:StringTokenizer
是一种读取由标记分隔的字符串输入的更快方法。
查看下面的示例以读取以空格分隔的整数字符串并存储在 arraylist 中,
String str = input.readLine(); //read string of integers using BufferedReader e.g. "1 2 3 4"
List<Integer> list = new ArrayList<>();
StringTokenizer st = new StringTokenizer(str, " ");
while (st.hasMoreTokens())
list.add(Integer.parseInt(st.nextToken()));
【讨论】:
【参考方案6】:从编程的角度来看,这个定制的 Scan 和 Print 类比 Java 内置的 Scanner 和 BufferedReader 类要好得多。
import java.io.InputStream;
import java.util.InputMismatchException;
import java.io.IOException;
public class Scan
private byte[] buf = new byte[1024];
private int total;
private int index;
private InputStream in;
public Scan()
in = System.in;
public int scan() throws IOException
if(total < 0)
throw new InputMismatchException();
if(index >= total)
index = 0;
total = in.read(buf);
if(total <= 0)
return -1;
return buf[index++];
public int scanInt() throws IOException
int integer = 0;
int n = scan();
while(isWhiteSpace(n)) /* remove starting white spaces */
n = scan();
int neg = 1;
if(n == '-')
neg = -1;
n = scan();
while(!isWhiteSpace(n))
if(n >= '0' && n <= '9')
integer *= 10;
integer += n-'0';
n = scan();
else
throw new InputMismatchException();
return neg*integer;
public String scanString()throws IOException
StringBuilder sb = new StringBuilder();
int n = scan();
while(isWhiteSpace(n))
n = scan();
while(!isWhiteSpace(n))
sb.append((char)n);
n = scan();
return sb.toString();
public double scanDouble()throws IOException
double doub=0;
int n=scan();
while(isWhiteSpace(n))
n=scan();
int neg=1;
if(n=='-')
neg=-1;
n=scan();
while(!isWhiteSpace(n)&& n != '.')
if(n>='0'&&n<='9')
doub*=10;
doub+=n-'0';
n=scan();
else throw new InputMismatchException();
if(n=='.')
n=scan();
double temp=1;
while(!isWhiteSpace(n))
if(n>='0'&&n<='9')
temp/=10;
doub+=(n-'0')*temp;
n=scan();
else throw new InputMismatchException();
return doub*neg;
public boolean isWhiteSpace(int n)
if(n == ' ' || n == '\n' || n == '\r' || n == '\t' || n == -1)
return true;
return false;
public void close()throws IOException
in.close();
而自定义的Print类可以如下
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class Print
private BufferedWriter bw;
public Print()
this.bw = new BufferedWriter(new OutputStreamWriter(System.out));
public void print(Object object)throws IOException
bw.append("" + object);
public void println(Object object)throws IOException
print(object);
bw.append("\n");
public void close()throws IOException
bw.close();
【讨论】:
【参考方案7】:您可以使用 BufferedReader 来读取数据
BufferedReader inp = new BufferedReader(new InputStreamReader(System.in));
int t = Integer.parseInt(inp.readLine());
while(t-->0)
int n = Integer.parseInt(inp.readLine());
int[] arr = new int[n];
String line = inp.readLine();
String[] str = line.trim().split("\\s+");
for(int i=0;i<n;i++)
arr[i] = Integer.parseInt(str[i]);
对于打印使用 StringBuffer
StringBuffer sb = new StringBuffer();
for(int i=0;i<n;i++)
sb.append(arr[i]+" ");
System.out.println(sb);
【讨论】:
【参考方案8】:这里是完整版的快速阅读器和编写器。我也使用了缓冲。
import java.io.*;
import java.util.*;
public class FastReader
private static StringTokenizer st;
private static BufferedReader in;
private static PrintWriter pw;
public static void main(String[] args) throws IOException
in = new BufferedReader(new InputStreamReader(System.in));
pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
st = new StringTokenizer("");
pw.close();
private static int nextInt() throws IOException
return Integer.parseInt(next());
private static long nextLong() throws IOException
return Long.parseLong(next());
private static double nextDouble() throws IOException
return Double.parseDouble(next());
private static String next() throws IOException
while(!st.hasMoreElements() || st == null)
st = new StringTokenizer(in.readLine());
return st.nextToken();
【讨论】:
【参考方案9】:一次又一次地从磁盘读取会使扫描程序变慢。我喜欢结合使用 BufferedReader 和 Scanner 来获得两全其美的效果。即 BufferredReader 的速度和扫描仪丰富易用的 API。
Scanner scanner = new Scanner(new BufferedReader(new InputStreamReader(System.in)));
【讨论】:
以上是关于从 Java 中读取 System.in 的最快方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
java从键盘接受字符串行BufferedReader(InputStreamReader(System.in))这样用BufferedReader对象读取行
java中DataInputStream如何读取我们控制台的字符串
BufferReader buf_reader=new BuffReader(new InputStreamRead()System.in);请问这个Java是啥意思啊 3Q