![](/img/trans.png)
[英]Java - Millions of records, HashMap throws OutOfMemoryError
[英]Calculate statistics on +20millions records in Java
我有csv文件(600 MB)和2000万行。 我需要读取所有这些数据,从中创建Java对象列表,并在对象字段上计算一些指标,例如平均值,中位数,最大值,总和和其他统计信息。 用Java做到这一点的最佳方法是什么? 我尝试了简单的.forEach循环,并花了一段时间(20分钟)对其进行迭代。
更新:我使用BufferReader读取数据并将csv文件转换为某些Java类的对象列表。 非常快。 它在forEach循环中停留了20分钟,在该循环中,我尝试遍历这2000万个对象列表,并将它们分为3个列表,具体取决于当前对象中的值。 因此,基本上,我遍历整个列表一次,并且具有if / else条件,在该条件下,我检查对象中的某些字段是否等于“ X”,“ Y”或“ Z”,并根据答案将其分开将这20万条记录分成3个列表。
然后,对于这三个列表,我需要计算不同的统计数据:例如中位数,平均值,总和等
广泛处理超过600Mb的数据量后,我可以提出以下两种说法:
但是,您应该做的是确保将数据读入列式连续数组,并使用直接在列式数据的那些连续数组上运行的方法。
因为它是一个csv文件,是按行存储的,所以最好将它按块读取为字节数组,然后将其解析为按列的预分配表示形式。
将600Mb的块读入SSD的内存中大约需要几秒钟,对其进行解析将取决于您的算法(但是必须能够在该结构中立即查找是至关重要的)。 在内存方面,您将使用大约600Mb的三倍,但是对于16Gb机器而言,这无疑是个明智的选择。
因此,不要急于使用SQL或切片文件,也不要将每个单元都实例化为Java对象。 也就是说,在这种例外情况下 ,您不需要Java对象列表 ,而需要double[]
等。尽管您预先分配了确切的大小,但是可以使用ArrayList
。 其他标准收藏品将杀死您。
说了这么多,我宁愿推荐python
和numpy
来完成任务,而不是Java。 Java在对象方面很有用,而在连续内存块和相应的操作方面则不如Java。 C++
甚至可以做到R
我强烈建议不要将全部600MB加载到RAM中并将其用作Java对象。 正如您所说的,这需要花费很多时间。
您可以做什么:
使用SQL:将数据转换为数据库,然后在该数据库上执行搜索查询。 不要循环访问RAM中的所有对象。 这将使您的应用程序性能很差。
SQL经过优化,可处理大量数据并对其执行查询。
阅读有关Java中的数据库管理的更多信息: JDBC基础
听起来就像您在向列表中添加内容时,程序只是耗尽了内存。 如果您接近分配给JVM的内存限制,则大部分时间将由垃圾收集器花费,以尽其所能防止内存不足。
您应该使用快速 CSV库(例如univocity解析器)遍历每一行并执行所需的计算,而不必将所有内容存储在内存中。 像这样使用它:
CsvParserSettings parserSettings = new CsvParserSettings(); //configure the parser
parserSettings.selectFields("column3", "column1", "column10"); //only read values from columns you need
CsvParser parser = new CsvParser(parserSettings);
//use this if you just need plain strings
for(String[] row : parser.iterate(new File("/path/to/your.csv"))){
//do stuff with the row
}
//or use records to get values ready for calculation
for(Record record : parser.iterateRecords(new File("/path/to/your.csv"))){
int someValue = record.getInt("columnName");
//perform calculations
}
如果出于某种原因您需要多次遍历所有行,只需将数据存储在庞大的列表中即可。 在这种情况下,可以使用-Xms8G -Xmx8G
类的程序为程序分配更多的内存。 请记住, ArrayList
的大小不能超过Integer.MAX_VALUE
因此即使您有足够的内存,这也是下一个限制。
如果您确实需要一个列表,则可以使用如下所示的解析器:
List<Record> twentyMillionRecords = parser.parseAllRecords(new File("/path/to/your.csv"), 20_000_000);
否则,最好的选择是根据需要多次运行解析器。 我建议的解析器每次都需要花费几秒钟来浏览文件。
希望这可以帮助
免责声明:我是这个图书馆的作者。 它是开源和免费的(apache 2.0许可)
我敢打赌,大部分时间都花在读取数据上。 拥有BufferedReader应该可以大大加快速度。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.