在Java中读取、比较和合并多个文件
鉴于存在一些文件Customer-1.txt、Customer-2.txt和Customer-3.txt,这些文件具有以下内容: Customer-1.txt在Java中读取、比较和合并多个文件,java,memory,io,cpu,Java,Memory,Io,Cpu,鉴于存在一些文件Customer-1.txt、Customer-2.txt和Customer-3.txt,这些文件具有以下内容: Customer-1.txt 1|1|MARY|SMITH 2|1|PATRICIA|JOHNSON 4|2|BARBARA|JONES Customer-2.txt 1|1|MARY|SMITH 2|1|PATRICIA|JOHNSON 3|1|LINDA|WILLIAMS 4|2|BARBARA|JONES Customer-3.txt
1|1|MARY|SMITH
2|1|PATRICIA|JOHNSON
4|2|BARBARA|JONES
Customer-2.txt
1|1|MARY|SMITH
2|1|PATRICIA|JOHNSON
3|1|LINDA|WILLIAMS
4|2|BARBARA|JONES
Customer-3.txt
2|1|PATRICIA|JOHNSON
3|1|LINDA|WILLIAMS
5|2|ALEXANDER|ANDERSON
这些文件有很多重复的数据,但每个文件都可能包含一些唯一的数据
考虑到实际的文件是经过排序的,很大(每个文件有几GB),而且有很多文件
那么什么是:a) 内存最便宜
b) cpu最便宜的
c) 最快的
在Java中,从这三个文件中创建一个文件,该文件将包含每个文件的所有唯一数据,并按如下方式排序和连接: Customer-final.txt
1|1|MARY|SMITH
2|1|PATRICIA|JOHNSON
3|1|LINDA|WILLIAMS
4|2|BARBARA|JONES
5|2|ALEXANDER|ANDERSON
我研究了以下解决方案,但我想知道是否可能使用FileInputStream和/或非spring批处理解决方案来实现
由于文件的大小和缺少实际数据库,使用内存或真实数据库连接它们的解决方案对于我的用例不可行。这可能会有帮助:
public static void main(String[] args) {
String files[] = {"Customer-1.txt", "Customer-2.txt", "Customer-3.txt"};
HashMap<Integer, String> customers = new HashMap<Integer, String>();
try {
String line;
for(int i = 0; i < files.length; i++) {
BufferedReader reader = new BufferedReader(new FileReader("data/" + files[i]));
while((line = reader.readLine()) != null) {
Integer uuid = Integer.valueOf(line.split("|")[0]);
customers.put(uuid, line);
}
reader.close();
}
BufferedWriter writer = new BufferedWriter(new FileWriter("data/Customer-final.txt"));
Iterator<String> it = customers.values().iterator();
while(it.hasNext()) writer.write(it.next() + "\n");
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
publicstaticvoidmain(字符串[]args){
字符串文件[]={“Customer-1.txt”、“Customer-2.txt”、“Customer-3.txt”};
HashMap客户=新的HashMap();
试一试{
弦线;
对于(int i=0;i
如果您有任何疑问,请询问我。由于输入文件已经排序,因此对文件进行简单的并行迭代,合并它们的内容,是最节省内存、最节省cpu和最快的方法 这是一种多路合并联接,即不带“sort”的排序合并联接,可消除重复项,类似于SQL
DISTINCT
这是一个可以执行无限数量输入文件的版本(不管怎样,只要有打开的文件就可以)。它使用一个helper类来暂存每个输入文件的下一行,因此每行只需解析一次前导ID值
private static void merge(StringWriter out, BufferedReader ... in) throws IOException {
CustomerReader[] customerReader = new CustomerReader[in.length];
for (int i = 0; i < in.length; i++)
customerReader[i] = new CustomerReader(in[i]);
merge(out, customerReader);
}
private static void merge(StringWriter out, CustomerReader ... in) throws IOException {
List<CustomerReader> min = new ArrayList<>(in.length);
for (;;) {
min.clear();
for (CustomerReader reader : in)
if (reader.hasData()) {
int cmp = (min.isEmpty() ? 0 : reader.compareTo(min.get(0)));
if (cmp < 0)
min.clear();
if (cmp <= 0)
min.add(reader);
}
if (min.isEmpty())
break; // all done
// optional: Verify that lines that compared equal by ID are entirely equal
out.write(min.get(0).getCustomerLine());
out.write(System.lineSeparator());
for (CustomerReader reader : min)
reader.readNext();
}
}
private static final class CustomerReader implements Comparable<CustomerReader> {
private BufferedReader in;
private String customerLine;
private int customerId;
CustomerReader(BufferedReader in) throws IOException {
this.in = in;
readNext();
}
void readNext() throws IOException {
if ((this.customerLine = this.in.readLine()) == null)
this.customerId = Integer.MAX_VALUE;
else
this.customerId = Integer.parseInt(this.customerLine.substring(0, this.customerLine.indexOf('|')));
}
boolean hasData() {
return (this.customerLine != null);
}
String getCustomerLine() {
return this.customerLine;
}
@Override
public int compareTo(CustomerReader that) {
// Order by customerId only. Inconsistent with equals()
return Integer.compare(this.customerId, that.customerId);
}
}
输出
String file1data = "1|1|MARY|SMITH\n" +
"2|1|PATRICIA|JOHNSON\n" +
"4|2|BARBARA|JONES\n";
String file2data = "1|1|MARY|SMITH\n" +
"2|1|PATRICIA|JOHNSON\n" +
"3|1|LINDA|WILLIAMS\n" +
"4|2|BARBARA|JONES\n";
String file3data = "2|1|PATRICIA|JOHNSON\n" +
"3|1|LINDA|WILLIAMS\n" +
"5|2|ALEXANDER|ANDERSON\n";
try (
BufferedReader in1 = new BufferedReader(new StringReader(file1data));
BufferedReader in2 = new BufferedReader(new StringReader(file2data));
BufferedReader in3 = new BufferedReader(new StringReader(file3data));
StringWriter out = new StringWriter();
) {
merge(out, in1, in2, in3);
System.out.print(out);
}
1|1|MARY|SMITH
2|1|PATRICIA|JOHNSON
3|1|LINDA|WILLIAMS
4|2|BARBARA|JONES
5|2|ALEXANDER|ANDERSON
代码纯粹通过ID值进行合并,并没有验证行的其余部分是否实际相等。如果需要,在
可选的注释处插入代码以检查是否存在此问题。是客户ID的第一个值,并且文件是否已按该第一个值(数字)排序,如您的示例所示?是的,您是正确的,它们已排序。谢谢你的提醒,我会把它添加到问题描述中。我不认为你不阅读文件内容就可以做到这一点。您必须读取每个文件,然后将输出写入一个单独的文件。如果我理解正确,您可以使用类似于合并排序的方法,即从每个文件中读取一行,找出最小的一行,并将其写入一些输出流
。唯一的区别是你有2个以上的文件,你需要删除重复的文件。这将把所有(唯一的)行加载到内存中,所以它肯定不是“最便宜的内存”。使用split()。由于您将行存储在一个哈希集中
,因此输出将不会被排序。非常详细的答案和我要查找的内容。实际上,我的所有文件都有一个经过排序的唯一标识符(客户文件,以及所有其他文件,例如Actor文件)。我是否正确理解了,对于每个“组”文件,我必须调用文件组的合并函数?这是正确的。对于需要合并到单个输出文件的每一组类似文件,您可以调用merge,例如,一次对客户文件的合并调用,另一次对参与者文件的合并调用,等等……仅供参考:由于您的文件很大,并且可能一次写入一个,因此它们可能都存储在硬盘上的连续区域中。当读取3个文件并并行写入一个文件时,硬盘臂可能会移动很多。您可以通过将BufferedReader
和BufferedWriter
对象的缓冲区大小从默认的8k增加到更高的值,例如64k(甚至1M?),将其最小化。将其增加得太高只会浪费内存而不会提高性能。非常有用的注释。当前最大的硬件瓶颈是内存,因此我将尝试使用一些不同的缓冲区大小,看看什么最适合我的场景。谢谢安德烈亚斯的回答。