我的文字文件包含以下格式中的1 000 002
个数字:
123 456
1 2 3 4 5 6 .... 999999 100000
现在我需要读取该数据并将其分配给int
变量(前两个数字)和其余所有( 1 000 000 数字)到数组{{1 }}
这不是一项艰巨的任务,但是 - 它的速度非常慢。
int[]
:java.util.Scanner
它可以作为例外工作但执行时需要 7500 ms 。我需要在几百毫秒内获取该数据。
Scanner stdin = new Scanner(new File("./path"));
int n = stdin.nextInt();
int t = stdin.nextInt();
int array[] = new array[n];
for (int i = 0; i < n; i++) {
array[i] = stdin.nextInt();
}
:使用java.io.BufferedReader
和BufferedReader.readLine()
我在 1700 ms 时得到了相同的结果,但它仍然太多了。
如何在不到1秒的时间内读取该数据量?最终结果应该等于:
String.split()
int n = 123;
int t = 456;
int array[] = { 1, 2, 3, 4, ..., 999999, 100000 };
解决方案很快(大约需要1400毫秒),但它仍然太慢:
StreamTokenizer
PS。无需验证。我完全确定StreamTokenizer st = new StreamTokenizer(new FileReader("./test_grz"));
st.nextToken();
int n = (int) st.nval;
st.nextToken();
int t = (int) st.nval;
int array[] = new int[n];
for (int i = 0; st.nextToken() != StreamTokenizer.TT_EOF; i++) {
array[i] = (int) st.nval;
}
文件中的数据是否正确。
答案 0 :(得分:12)
感谢您的回答,但我已经找到了符合我标准的方法:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("./path"));
int n = readInt(bis);
int t = readInt(bis);
int array[] = new int[n];
for (int i = 0; i < n; i++) {
array[i] = readInt(bis);
}
private static int readInt(InputStream in) throws IOException {
int ret = 0;
boolean dig = false;
for (int c = 0; (c = in.read()) != -1; ) {
if (c >= '0' && c <= '9') {
dig = true;
ret = ret * 10 + c - '0';
} else if (dig) break;
}
return ret;
}
只需要 300 ms 即可读取1百万个整数!
答案 1 :(得分:2)
StreamTokenizer
可能会更快,如建议here。
答案 2 :(得分:2)
您可以使用StreamTokenizer
:
BufferedReader
结果的时间
Reader r = null;
try {
r = new BufferedReader(new FileReader(file));
final StreamTokenizer st = new StreamTokenizer(r);
...
} finally {
if (r != null)
r.close();
}
此外,请不要忘记关闭您的文件,正如我在此处所示。
您还可以为自己的目的使用自定义标记器,从而节省更多时间:
public class CustomTokenizer {
private final Reader r;
public CustomTokenizer(final Reader r) {
this.r = r;
}
public int nextInt() throws IOException {
int i = r.read();
if (i == -1)
throw new EOFException();
char c = (char) i;
// Skip any whitespace
while (c == ' ' || c == '\n' || c == '\r') {
i = r.read();
if (i == -1)
throw new EOFException();
c = (char) i;
}
int result = (c - '0');
while ((i = r.read()) >= 0) {
c = (char) i;
if (c == ' ' || c == '\n' || c == '\r')
break;
result = result * 10 + (c - '0');
}
return result;
}
}
请记住为此使用BufferedReader
。此自定义标记生成器假定输入数据始终完全有效,并且仅包含空格,新行和数字。
如果您仔细阅读这些结果并且这些结果没有太大变化,您应该保存数组并跟踪上次修改文件的时间。然后,如果文件没有更改,只需使用数组的缓存副本,这将显着加快结果。例如:
public class ArrayRetriever {
private File inputFile;
private long lastModified;
private int[] lastResult;
public ArrayRetriever(File file) {
this.inputFile = file;
}
public int[] getResult() {
if (lastResult != null && inputFile.lastModified() == lastModified)
return lastResult;
lastModified = inputFile.lastModified();
// do logic to actually read the file here
lastResult = array; // the array variable from your examples
return lastResult;
}
}
答案 3 :(得分:1)
你在电脑里有多少内存?您可能会遇到GC问题。
最好的办法是尽可能一次处理一行数据。不要将其加载到数组中。加载您需要的,处理,写出来并继续。
这将减少您的内存占用并仍然使用相同数量的文件IO
答案 4 :(得分:1)
可以重新格式化输入,以便每个整数都在一个单独的行上(而不是一个带有一百万个整数的长行),由于行更智能的缓冲,你应该看到使用Integer.parseInt(BufferedReader.readLine())
大大提高了性能而不必将长字符串拆分为单独的字符串数组。
编辑:我对此进行了测试并设法将seq 1 1000000
生成的输出读取到int
的数组中,不到半秒钟,但当然这取决于机。
答案 5 :(得分:0)
我会扩展FilterReader并解析在read()方法中读取的字符串。让getNextNumber方法返回数字。代码留给读者练习。
答案 6 :(得分:0)
在BufferedReader上使用StreamTokenizer将为您提供相当好的性能。你不应该编写自己的readInt()函数。
以下是我用于进行本地性能测试的代码:
/**
* Created by zhenhua.xu on 11/27/16.
*/
public class MyReader {
private static final String FILE_NAME = "./1m_numbers.txt";
private static final int n = 1000000;
public static void main(String[] args) {
try {
readByScanner();
readByStreamTokenizer();
readByStreamTokenizerOnBufferedReader();
readByBufferedInputStream();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void readByScanner() throws Exception {
long startTime = System.currentTimeMillis();
Scanner stdin = new Scanner(new File(FILE_NAME));
int array[] = new int[n];
for (int i = 0; i < n; i++) {
array[i] = stdin.nextInt();
}
long endTime = System.currentTimeMillis();
System.out.println(String.format("Total time by Scanner: %d ms", endTime - startTime));
}
public static void readByStreamTokenizer() throws Exception {
long startTime = System.currentTimeMillis();
StreamTokenizer st = new StreamTokenizer(new FileReader(FILE_NAME));
int array[] = new int[n];
for (int i = 0; st.nextToken() != StreamTokenizer.TT_EOF; i++) {
array[i] = (int) st.nval;
}
long endTime = System.currentTimeMillis();
System.out.println(String.format("Total time by StreamTokenizer: %d ms", endTime - startTime));
}
public static void readByStreamTokenizerOnBufferedReader() throws Exception {
long startTime = System.currentTimeMillis();
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new FileReader(FILE_NAME)));
int array[] = new int[n];
for (int i = 0; st.nextToken() != StreamTokenizer.TT_EOF; i++) {
array[i] = (int) st.nval;
}
long endTime = System.currentTimeMillis();
System.out.println(String.format("Total time by StreamTokenizer with BufferedReader: %d ms", endTime - startTime));
}
public static void readByBufferedInputStream() throws Exception {
long startTime = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(FILE_NAME));
int array[] = new int[n];
for (int i = 0; i < n; i++) {
array[i] = readInt(bis);
}
long endTime = System.currentTimeMillis();
System.out.println(String.format("Total time with BufferedInputStream: %d ms", endTime - startTime));
}
private static int readInt(InputStream in) throws IOException {
int ret = 0;
boolean dig = false;
for (int c = 0; (c = in.read()) != -1; ) {
if (c >= '0' && c <= '9') {
dig = true;
ret = ret * 10 + c - '0';
} else if (dig) break;
}
return ret;
}
我得到的结果: