java - 通过 Java 中的键聚合文件中的键值行

标签 java memory hashmap guava key-value

我有一个巨大的文件,由约 8 亿行 (60g) 组成。行可以是重复的,由一个 id 和一个值组成。例如:

id1   valueA
id1   valueB 
id2   valueA 
id3   valueC
id3   valueA
id3   valueC

注意:ID 没有像示例中那样按顺序(和分组)。

我想通过键聚合行,以这种方式:

id1   valueA,valueB
id2   valueA
id3   valueC,valueA

有 5000 个可能的值。

该文件不适合内存,所以我不能使用简单的 Java 集合。 此外,大部分行都是单一的(例如 id2),它们应该直接写在输出文件中。

出于这个原因,我的第一个解决方案是对文件进行两次迭代:

  • 在第一次迭代中,我存储了两个结构,只有 ID,没有值:
    • 单值 ID (S1)
    • 多值 id (S2)
  • 在第二次迭代中,从内存中丢弃单值 id (S1) 后,我可以直接将单值 id-value 对写入输出文件,检查它们是否不在多值 id 中 (S2)

问题是由于内存限制,我无法完成第一次迭代。

我知道这个问题可能会以多种方式出现(键值存储、map reduce、外部排序)。

我的问题是什么方法可以更适应使用和快速实现?这是一个只有一次的过程,我更喜欢使用 Java 方法(而不是外部排序)。

最佳答案

如前所述(这很快!),合并排序是一种方法。具体来说,按 id 在本地排序,例如,每 100 万行。然后将本地排序的行保存到更小的文件中。然后重复地将较小的、已排序的文件成对合并为一个大的已排序文件。您可以在合并较小的文件时进行聚合。

直觉是,当您合并 2 个排序列表时,您维护 2 个指针,每个列表一个,并在进行排序时进行排序。您无需加载完整列表。这允许您缓冲大文件并立即缓冲合并结果。

这是在内存中排序并输出到文件的示例代码:

private void sortAndSave(List<String> lines, Path fileOut) throws IOException {
    Collections.sort(lines, comparator);
    Files.write(fileOut, lines);
}

这是在本地排序并将结果保存到较小文件中的示例代码:

// Sort once we collect 1000000 lines
final int cutoff = 1000000;
final List<String> lines = new ArrayList<>();
int fileCount = 0;
try (BufferedReader reader = Files.newBufferedReader(fileIn, charset)) {
    String line = reader.readLine();
    while (line != null) {
        lines.add(line);
        if (lines.size() > cutoff) {
            fileCount++;
            sortAndSave(lines, Paths.get("fileOut" + fileCount));
            lines.clear();
        }
        line = reader.readLine();
    }
    if (lines.size() > 0) {
        fileCount++;
        sortAndSave(lines, fileOut, Paths.get("fileOut" + fileCount));
    }
}

这是合并排序 2 个文件的示例代码:

try (BufferedReader reader1 = Files.newBufferedReader(file1, charset);
     BufferedReader reader1 = Files.newBufferedReader(file2, charset);
     BufferedWriter writer = Files.newBufferedWriter(fileOut, charset)) {
    String line1 = reader1.read();
    String line2 = reader2.read();
    while (line1 != null && line2 != null) {
        if (comparator.compare(line1, line2) < 0) {
            writer.write(line2);
            line2 = reader2.read();
        } else {
            writer.write(line1);
            line1 = reader1.read();
        }
    }
    if (line1 != null) {
        // TODO: Merge in the remaining lines of file1
    } else if (line2 != null {
        // TODO: Merge in the remaining lines of file2
    }
}

关于java - 通过 Java 中的键聚合文件中的键值行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34916965/

相关文章:

c - Centos中如何限制进程可以使用的最大内存?

Clojure简单马尔可夫数据变换

java - 文件中 HashMap 字符串出现次数 [JAVA]

javascript - Guava 的 ComparisonChain 转换为 JavaScript

java - Spring AMQP RabbitMQ 代码中的发送方是否不提供交换类型?

java - 如何将每个集合存储在不相交的集合森林中?

c - 复杂类型的 mmap 问题

ios - Xcode 分配工具生成

java - 可以控制 Java 中对象的身份吗?如果可以,如何控制?

java - Selenium Java 不断刷新页面直到元素可见