我有一个程序从列表中获取每个项目,并将其与另一个列表中的所有其他项目进行比较。它到目前为止工作正常,但数据变得越来越大,并且将超过系统内存。
我想知道比较两个非常大的列表(每个列表可能是5-10 GB)的最佳方法是什么?
这是我正在做的一个非常简单的例子(除了列表很大并且for循环中的值实际上正在被处理/比较)。
import java.util.Collection;
import java.util.HashSet;
import java.util.Arrays;
public class comparelists {
public static void main( String [] args ) {
String[] listOne = {"a","b",
"c","d",
"e","f",
"g","h",
"i","j",
"k","l"};
String[] listTwo = {"one",
"two",
"three",
"four",
"five","six","seven"};
for(int listOneItem=0; listOneItem<listOne.length; listOneItem++){
for (int listTwoItem=0; listTwoItem<listTwo.length; listTwoItem++) {
System.out.println(listOne[listOneItem] + " " + listTwo[listTwoItem]);
}
}
}
}
我意识到这里必须有一些磁盘IO因为它不适合内存而我的初始方法是将两个列表保存为文件并从listOne中保存一堆行然后流式传输listTwo的整个文件然后获取来自listOne的更多行等等。有没有更好的办法?或者像我上面那样访问列表的Java方法,但是根据需要将其交换到磁盘?
答案 0 :(得分:2)
您可以将大数据放在平面文件中,然后一次从文件中流式传输一项数据。这样,在任何给定时间,只有两项数据存储在内存中。
显然,这不会赢得任何效率奖励,但这是一个简单的例子,它使用的数据文件在文本文件中每行包含一个项目:
BufferedReader readerA = new BufferedReader(new FileReader("listA.txt"));
String lineA;
while ((lineA = readerA.readLine()) != null)
{
BufferedReader readerB = new BufferedReader(new FileReader("listB.txt"));
String lineB;
while ((lineB = readerB.readLine()) != null)
{
compare(lineA, lineB);
}
// TODO: ensure .close() is called on readerB
}
// TODO: ensure .close() is called on readerA
如果你正在使用的数据太复杂而不能轻易地在文本文件中每行存储一个项目,那么你可以使用ObjectInputStream和ObjectOutputStream做类似的事情,它可以一次读取和写入一个Java对象。文件。
如果您可以设法将listB放入内存中,那么显然您可以在第一个循环中保存相当多的磁盘访问权限。如果您有足够的重复数据,则Memoization可能会帮助您将listB放入内存中。
项目的比较也是一个教科书示例,可以通过使用并行化加速问题。例如。将数据比较工作交给工作线程,以便文件读取线程可以专注于最大化磁盘的吞吐量。
答案 1 :(得分:0)
使用Flyweight模式。这是一个链接:
答案 2 :(得分:0)
我可以看到您的目标是在2个非常大的列表的Cartesian product上执行某些操作。
我认为你担心的低效率是将文件从文件读入主存的时间。
如何将列表分成可以加载到内存中的块。
说l1[0]
是l1
中前1000个项目的列表,l1[1]
是下一个1000个项目的列表。
然后你要比较:
l1[0] with l2[0]
l1[0] with l2[1]
l1[0] with l2[2]
...
l1[0] with l2[0]
l1[1] with l2[1]
l1[2] with l2[2]
...
通过较少的文件读取来实现相同的总效果。