将大型文本文件加载到整数数组中的最快方式

4
我有一个大文本文件(+100MB),每行都是一个整数(包含一千万个数字)。当然,大小和数量可能会改变,所以我事先不知道这些。
我想将文件加载到一个int[]中,并使过程尽可能快。首先,我想到了以下解决方案:
public int[] fileToArray(String fileName) throws IOException
{
    List<String> list = Files.readAllLines(Paths.get(fileName));
    int[] res = new int[list.size()];
    int pos = 0;
    for (String line: list)
    {
        res[pos++] = Integer.parseInt(line);
    }
    return res;
}

速度相当快,只需5.5秒。其中,readAllLines 调用耗时5.1秒,循环耗时0.4秒。

然后我决定尝试使用 BufferedReader,并得到了这种不同的解决方案:

public int[] fileToArray(String fileName) throws IOException
{
    BufferedReader bufferedReader = new BufferedReader(new FileReader(new File(fileName)));
    ArrayList<Integer> ints = new ArrayList<Integer>();
    String line;
    while ((line = bufferedReader.readLine()) != null)
    {
        ints.add(Integer.parseInt(line));
    }
    bufferedReader.close();

    int[] res = new int[ints.size()];
    int pos = 0;
    for (Integer i: ints)
    {
        res[pos++] = i.intValue();
    }
    return res;
}

这次更快了!只用了3秒钟的while循环和不到0.1秒钟的for循环,总共只用了3.1秒钟。
我知道在这里优化的空间不多,至少在时间上是这样,但是使用ArrayList然后再使用int[]对我来说似乎太占内存了。
有什么想法可以让它更快,或者避免使用中间的ArrayList吗?
仅供比较,我使用FreePascal在1.9秒钟内完成了相同的任务[请参见编辑],使用了TStringList类和StrToInt函数。
编辑:由于我用Java方法得到了一个相当短的时间,所以我不得不改进FreePascal方法。330~360毫秒。

1
看起来你已经收集了一些很好的指标。你可能想要看一下https://dev59.com/gmYs5IYBdhLWcg3wFP54。 - Jameson
你可以尝试这个 ArrayList<Integer> ints = new ArrayList<Integer>(); Integer[] res = ints.toArray(new Integer[ints.size()]); - ravthiru
1
你能通过获取文件大小来近似计算文件中int的数量吗?你可以将其作为构造函数中ArrayList<>的初始容量传递,这样也许就不需要多次扩容了。 - WW.
@WW。我试过了,没有明显的差别。 - mclopez
你的FreePascal是否使用Unicode(Java内部使用一种类似UTF-16的编码方式,因此每个字符占用两个字节;Java 9将提供更紧凑的Latin-1字符串编码方式)?在Java中,“String”是一级对象,这会带来一些成本。 +++ 你的平台编码是什么?使用“new InputStreamReader(new FileInputStream(...), encoding)”可能会提高一些速度。 - maaartinus
显示剩余2条评论
1个回答

7
如果您正在使用Java 8,您可以通过使用lines()并将其映射到int,然后将值收集到数组中来消除中间的ArrayList
您还应该使用try-with-resources进行适当的异常处理和自动关闭。
try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
    return br.lines()
             .mapToInt(Integer::parseInt)
             .toArray();
}

我不确定这样做是否更快,但肯定更容易维护。

编辑:显然要快得多。


我会很感兴趣,如果@mclopez能够为我们提供关于这个解决方案的性能信息。 - WW.
如果这对您的研究有帮助,这里使用的功能是“流”和方法引用。在研究过程中,您还应该研究lambda表达式。 - 4castle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接