读取HUGE csv文件时存储器问题,存储为Person对象,写入多个更干净/更小的CSV文件

时间:2014-02-07 20:36:41

标签: java csv out-of-memory hashtable large-files

我有两个带逗号分隔值的文本文件。一个是150MB,另一个是370MB,所以这些人有300万行数据。

一份文件包含有关软饮料偏好的信息,下一篇文章可能包含有关头发颜色的信息。

示例软饮料数据文件,但在真实文件中,UniqueNames不按顺序排列,日期也不是:

"UniqueName","softDrinkBrand","year"
"001","diet pepsi","2004"
"001","diet coke","2006"
"001","diet pepsi","2004"
"002","diet pepsi","2005"
"003","coca cola","2004"

基本上,有太多的数据行要使用excel,所以我想使用Person类创建Person对象来保存每个人的数据。

每个Person对象包含20个数组列表,2004-2013中每年有两个,例如,

...
private ArrayList<String> sodas2013= new ArrayList<String>();
private ArrayList<String> hairColors2013= new ArrayList<String>();
private ArrayList<String> sodas2014= new ArrayList<String>();
private ArrayList<String> hairColors2014= new ArrayList<String>();
...

我编写了一个程序,使用BufferedReader一次读取一个数据文件的行。 对于每一行,我清理数据(在逗号上拆分,删除引号......),然后,如果该特定uniqueID尚未在Hashtable中,我添加它,以及创建一个新的Person对象从我的Person类,然后我将我想要的数据存储到Person类的ArrayList中,如上所述。如果已经存在唯一ID,我只需调用Person方法来查看苏打水或头发颜色是否已存在于该特定年份的数组列表中(如csv文件中所述)。

目标是最终输出20个不同的csv文件,其中一个将人们与每年喝醉的苏打水绑在一起,一个用于当年的染发剂。它们看起来像这样......

2004文件使用上面的示例输入文件:

UID    pepsi    coca cola    diet pepsi    diet coke    etc
001    false    false    true    false    etc
002    false    false    false    false    etc
003    false    true    false    false    etc

现在,当我的测试文件每个只有100行时,这样可以很好地工作。我将所有数据保存在Person对象中,然后使用方法将Hashtable uniqueNames与uniqueSoftDrinkNames匹配,存储在Person对象中的年份用于写入具有personID行的文件,然后对于任何uniqueID尝试过的每个可能的soda都使用true / false任何一年。数据看起来像上面的信息。

所以,我知道代码可以运行并按照我的意愿行事。问题,现在是......

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Unknown Source)
at java.lang.String.<init>(Unknown Source)
at java.lang.StringBuffer.toString(Unknown Source)
at java.util.regex.Matcher.appendReplacement(Unknown Source)
at java.util.regex.Matcher.replaceAll(Unknown Source)
at java.lang.String.replaceAll(Unknown Source)
at CleanDataFiles.main(CleanDataFiles.java:43)

第43行是:

temp = temp.replaceAll("\"", "");

...这只是在用逗号分割一行后删除给定子串中的引号的简单点。

运行此程序的计算机大约需要十分钟才能达到此错误,并且两次运行程序时,它都给出了相同的错误和相同的行。

我正在逐行阅读CSV文档,所以当我读取文件时,我不会在巨大的字符串或任何内容中存储大量数据。我存储大量数据的唯一地方是在我的主类中的Hashtables中,我存储personID和personObjects,还有两个哈希表,我存储所有可能的头发颜色和所有可能的苏打水,并且在所有这些人物对象中,每个有二十位年底的所有苏打水和头发颜色信息的arraylists。

我的假设是内存问题在于存储这些成千上万个独特的人物对象以及与之相关的所有数据。也就是说,我在程序的一部分中得到了错误,我只是在阅读csv文件并清理单个条目......

无论如何,我的问题(你们都在等待这个!)

有更好的方法吗?而不是成千上万或几万个拥有所有这些数据的Person对象...我应该创建成千上万个Person文本文件,并在每次读取CSV文件的新行并查询是否打开和关闭它们信息是重复的还是新的,如果是新的,请将其添加到Person文件中?然后当完成所有操作时,打开每个人文件以读取信息,解释,然后一次一行地将其写入我的增长输出文件中,关闭该人文件,然后打开下一行的下一行,等?

或者,很抱歉,你认为,为了在清理和组织我的数据文件以进行进一步分析时不会耗尽内存,有没有更简单,更容易解决的问题?

我感谢任何帮助或建议!谢谢。

5 个答案:

答案 0 :(得分:0)

以下是一些想法。首先,可能是您的计算机上有足够的内存空间,但是没有为JVM分配足够的内存。尝试这样的事情:

java -Xms2048M -Xmx4096M YourProgram

当然,这些值取决于您的机器有多少内存。

另外,为什么在每个Person对象中使用String的ArrayList?如果您可以提前确定可能的苏打水或其他任何东西,那么您可以使用一个int数组,这样可以节省一些内存。

另一个选择是分段进行,首先做苏打水,当你完成时,做头发颜色,等等。

答案 1 :(得分:0)

我会说你的问题需要一个关系数据库。你将能够:

  • 将数据存储在磁盘上
  • 查询数据,加入指定的属性。

您甚至可以使用嵌入式数据库(http://www.h2database.com/ ---这个数据库包含在一个jar文件中,所以外部服务器程序)。

答案 2 :(得分:0)

您可以尝试导入轻量级数据库并使用sql查询所需信息。

答案 3 :(得分:0)

您可以使用java.util.Properties替换您的Hashtable。您可以使用将内容写入文件。来自javadocs:

  

写入条目后,将刷新输出流。此方法返回后,输出流保持打开状态。

或者您可以试用像JDBM2这样的磁盘备份HashMap。从它的网页

  

JDBM2的开发是为了支持天文计算,其数据不适合内存。它还为天文天文学家Asterope提供存储空间。

答案 4 :(得分:0)

减少内存使用量的一个优化是,不是将饮料类型字符串存储为字符串(在arraylist中),而是存储一个id。因此,您可以使用整数的ArrayList替换字符串的ArrayList。此外,Drink字符串到整数Id可能位于不同的HashMap中。您可能希望将Trove库用于原始集合。检查http://trove.starlight-systems.com/。 此外,当您检测到您获得一个人的完整信息时,它可能是被刷新到文件的候选者,它不再是在内存中。您可以在另一个HashMap中将该人标记为“完成”。

但最后,数据库是解决此问题的更好选择。像JavaDB这样的嵌入式DB就足够了。 也可以使用外部存储器缓存,例如Memcache,Redis。