分析来自大文件的数据时如何使用最小内存

时间:2016-10-31 12:02:34

标签: java algorithm

我有两个巨大的文件,每个文件将存储每行的整数值。格式如下所示:

File1:

3
4
11
30
0
...

文件2:

13
43
11
40
9
...

我需要找到一种方法来读取这两个文件并找到这两个文件的重复值。让我们考虑上面的例子,因为它出现在两个文件上,所以将打印值11。 从文件读取并循环值很容易。但问题是每个文件的行数远远超过Integer.MAXIMUM。所以我无法将整个文件读入内存,否则我将耗尽内存。有没有有效的方法来解决这个问题并消耗更少的内存?

EDIT1

我想找到一种解决方案,它不能将所有数据读入内存。最好是阅读文件的一部分并进行分析然后继续阅读。但我不知道如何实现我的目标。

4 个答案:

答案 0 :(得分:1)

一种最小化内存的简单方法是逐行读取每个文件("对于文件1的每一行,每行文件2比较行"),但可能需要很长时间瓶颈是磁盘I / O.它位于频谱的低端 - 最小内存,最长持续时间。 另一方面是创建巨大的堆并完全依赖于内存 - 最大内存,最短持续时间。 因此,最好的解决方案之间是平衡权衡,这通常需要更多的前期思考。

分析输入数据

如果两个文件只包含整数(32位)值,并且两个文件都包含> 2 ^ 32个条目,那么您肯定会在第一个文件中有重复项。

但是要确定,如果文件2包含重复项,我们只需要知道第一个文件中是否出现至少一次的相同值。那只是 1位信息我们必须保留在内存中,而不是整个值。 因为值范围限于整数,并且对于每个可能的值,我们必须知道它是否至少出现一次,我们需要一个2 ^ 32位的位集。

在单个long值中,我们可以存储64位64位文件的信息。因此,我们需要一个长度为67& 108> 864的数组来将文件1的整个最小出现信息存储在内存中。这需要大约512 MB的内存。

将文件1的表示读入内存后。您可以逐行扫描文件2并检查每个值,如果它在文件1中至少出现一次使用我们创建的数组,如果它是重复的则打印出来(或将其写入另一个文件或者数据结构)。

智能版

如果您想弄脏手,请继续阅读。如果您想使用JDK框中的内容,请使用大小为Integer.MAX_VALUE的{​​{3}}(感谢@Mzf)。

    BitSet bs = new BitSet(Integer.MAX_VALUE);
    bs.set(valueFromFile1); 
    boolean duplicate = bs.get(valueFromFile2); 

以胡须为主的男性版本:

查找数组的结构类似于

[ 0000 0001 0000 0000 ... , 0000 0000 1000 000 ..., ...]
          ^                           ^
          7 (0*64 + 7)                74 (1*64 + 8)

您需要具备的是从int值到索引位置和位偏移的转换。

int pos(int value){
    return value / 64;
}
long offsetMask(int value){
    return 1L << (value % 64)
}
boolean exists(long[] index, int value) {
    return (index[pos(value)] & offsetMask(value)) != 0;
}


long[] index = new long[67108864];

//read references file
Scanner sc = new Scanner(new File("file1"));
while (sc.hasNextInt()) {
    int value = sc.nextInt();
    index[pos(value)]  |= offsetMask(value);
}

//find duplicates
Scanner sc = new Scanner(new File("file2"));
while (sc.hasNextInt()) {
    int value = sc.nextInt();
    boolean result = exists(index, value)
    if(result) {
      System.out.println("Duplicate: " + value);
    }
}

(它与BitSet中完成的内容基本相同)

如果文件大小不足,因为值范围不会增加,则不需要超过512 MB。

答案 1 :(得分:0)

首先将每个文件从最小到最大排序。现在读取每个文件的第一行。相比。如果匹配,则记录重复,然后转到下一个文件(我选择文件A)。如果它们不匹配,请转到文件中较小的那一行的下一行。重复直到你到达一个文件的末尾。

答案 2 :(得分:0)

有几种方法可以解决这个问题。 但首先,您不必将整个文件读入内存,而是使用FileUtils逐行读取 - 请参阅几个示例here

你说线的数量大于Integer.MAXIMUM。 因此,您可以逐行读取第一个文件,将disticnt数字保存在Hashmap中。 现在逐行读取第二个文件,每行搜索地图中的数字 - 如果存在 - 打印它。 使用的最大内存量为Integer.MAXIMUM

如果你想减少内存占用,可以使用位图而不是map,而不是使用sizeof(int)* #distinct_numbers_in_first_file,你将使用const大小为Integer.MAXIMUM

如果您对输入有所了解 - 您可以选择适合您的解决方案

在这两种情况下,使用的内存都是有限的,不会将整个文件加载到内存中

答案 3 :(得分:0)

首先,您必须将输入分成带有序数据的较小文件。

这些文件应具有固定的数据范围,因此第一个文件的distinc值范围为0-1000000,第二个为1000001-2000000,依此类推。

如果你为每个输入执行此操作,最终会得到有序的&#34;桶和#34;给定范围内的值。你现在要做的就是比较那些&#34;桶和#34;再来一次得到重复值。

这会消耗磁盘空间以换取内存使用。

这就是我乍看之下尝试解决这个问题的方法。