在Java中,有没有办法随机化一个太大的文件以适应内存?

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(...)

不过,有了非常大的文件,我现在得到了OutOfMemoryError


你可以:

  • 使用更多内存或

  • 不是对实际的csv行进行无序处理,而是对行数进行集合,然后逐行读取输入文件(当然是缓冲的),并将该行写入所需的输出文件之一。


您可以存储映射文件并找到所有新行,存储在intlong的数组中。创建一个int索引数组,并对其进行无序处理。每行大约使用8-32个字节。如果这不适合内存,您也可以为这些数组使用内存映射文件。


下面是一个可能的算法:

  • 最大行数是可管理文件中的最大行数;
  • 从输入文件中读取max_行,使用原始算法随机化这些行,并将它们写入临时文件;
  • 重复2。直到输入文件中没有剩余的行;
  • 设n为0和您写入的临时文件数之间的随机数;从第n个临时文件中读取下一行;
  • 重复4。直到您从所有文件中读取所有行;前10000次将每一行写入第一个输出文件,然后将所有其他行写入另一个文件。

  • 首先,通过读取输入文件的内容(但不将其存储在内存中)来计算输入文件中的行数。调用行数N
  • 从数字1..N中随机抽取10000号样本。
  • 从一开始就读取输入文件。对于每一行,如果行号在步骤2中绘制的样本中,则将该行写入file1;否则,将其写入file2
  • 步骤2可以在执行步骤1时通过使用储层采样完成。


    使用某种索引方案。对csv文件进行一次分析以获得行数(不要在内存中保留任何内容,只需对其进行分析),然后从该范围中随机选择10000个数字(确保避免重复,例如使用Set或更复杂的文件)。然后再次解析csv,再次维护行的计数器。如果一个行号对应于您随机选择的一个编号,则将其输出到一个csv文件。将编号不匹配的行输出到另一个文件。


    如果您知道文件中的行数,并且要随机化完整的行,您只需按行数随机化,然后读取所选的行。只需通过随机类选择一个随机行并存储随机数列表,这样就不会选择两次。

    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会帮上忙的。只需随机化所选行的列表,然后按顺序访问它们。至于未关闭的代码,我编辑了上面的代码,只是忽略了所选的代码。