我有两个巨大的文件,每个文件将存储每行的整数值。格式如下所示:
File1:
3
4
11
30
0
...
文件2:
13
43
11
40
9
...
我需要找到一种方法来读取这两个文件并找到这两个文件的重复值。让我们考虑上面的例子,因为它出现在两个文件上,所以将打印值11。 从文件读取并循环值很容易。但问题是每个文件的行数远远超过Integer.MAXIMUM。所以我无法将整个文件读入内存,否则我将耗尽内存。有没有有效的方法来解决这个问题并消耗更少的内存?
EDIT1
我想找到一种解决方案,它不能将所有数据读入内存。最好是阅读文件的一部分并进行分析然后继续阅读。但我不知道如何实现我的目标。
答案 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;再来一次得到重复值。
这会消耗磁盘空间以换取内存使用。
这就是我乍看之下尝试解决这个问题的方法。