In Java, is there a way to randomize a file too large to fit into memory?
我要做的是无序排列行(从csv读取),然后将第一个随机的10000行打印到一个csv,其余的打印到一个单独的csv。用一个较小的文件我可以做一些像
1 2 3 | java.util.Collections.shuffle(...) for (int i=0; i < 10000; i++) printcsv(...) for (int i=10000; i < data.length; i++) printcsv(...) |
不过,有了非常大的文件,我现在得到了
你可以:
使用更多内存或
不是对实际的csv行进行无序处理,而是对行数进行集合,然后逐行读取输入文件(当然是缓冲的),并将该行写入所需的输出文件之一。
您可以存储映射文件并找到所有新行,存储在
下面是一个可能的算法:
步骤2可以在执行步骤1时通过使用储层采样完成。
使用某种索引方案。对csv文件进行一次分析以获得行数(不要在内存中保留任何内容,只需对其进行分析),然后从该范围中随机选择10000个数字(确保避免重复,例如使用
如果您知道文件中的行数,并且要随机化完整的行,您只需按行数随机化,然后读取所选的行。只需通过随机类选择一个随机行并存储随机数列表,这样就不会选择两次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | BufferedReader reader = new BufferedReader(new FileReader(new File("file.cvs"))); BufferedWriter chosen = new BufferedWriter(new FileWriter(new File("chosen.cvs"))); BufferedWriter notChosen = new BufferedWriter(new FileWriter(new File("notChosen.cvs"))); int numChosenRows = 10000; long numLines = 1000000000; Set<Long> chosenRows = new HashSet<Long>(numChosenRows+1, 1); for(int i = 0; i < numChosenRows; i++) { while(!chosenRows.add(nextLong(numLines))) { // add returns false if the value already exists in the Set } } String line; for(long lineNo = 0; (line = reader.readLine()) != null; lineNo++){ if(chosenRows.contains(lineNo)){ // Do nothing for the moment } else { notChosen.write(line); } } // Randomise the set of chosen rows // Use RandomAccessFile to write the rows in that order |
有关nextlong方法,请参见此答案,该方法将生成一个随机的长比例到特定大小。
编辑:作为大多数人,我忽略了以随机顺序编写随机选择行的要求。我想randomaccessfile会帮上忙的。只需随机化所选行的列表,然后按顺序访问它们。至于未关闭的代码,我编辑了上面的代码,只是忽略了所选的代码。