【问题标题】:How to extract unique lines in file > 10 GB with 4GB RAM如何使用 4GB RAM 提取大于 10 GB 的文件中的唯一行
【发布时间】:2015-11-04 21:02:31
【问题描述】:

我有一台具有 4 GB RAM 的 PC 和一个使用 10 GB 内存的文件。现在我想检查文件中的每一行是否都是唯一的,所以我编写了以下代码:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

public class Cleaner {

    public static void main(String[] args) throws IOException {
        if (args.length < 2) {
            System.out.println("Too less parameters!");
            return;
        }

        File file = new File(args[0]);
        BufferedReader buff = new BufferedReader(new FileReader(file));
        String line;
        Set<String> set = new HashSet<String>();
        while ((line = buff.readLine()) != null) {
            set.add(line);
        }
        FileWriter fw = new FileWriter(args[1]);
        for (String s : set) {
            fw.write(s + "\n");
            fw.flush();
        }
        fw.close();
        buff.close();

    }

}

但我得到了 OutOfMemoryException,所以我的问题是:
我应该如何更改我的代码以获取每行都是唯一的文件?
提前感谢您的帮助。

【问题讨论】:

  • 分割成块并比较成对。或者散列每一行,然后将散列与行一起存储。
  • 散列的问题是每一行只是一个散列,我应该如何分块我可能会错过一些重复的行。
  • 看看 RandomAccessFile,您可以从 RandomAccessFile 'a' 中读取第 1 行,并将其与 RandomAccessFile 'b' 的所有其他行进行比较。之后读取第 2 行,依此类推
  • 我会选择cat bigfile.txt | sort | uniq &gt; uniq.txt,但这当然不是 Java 解决方案。
  • @LeonidGlanz 这可能会有所帮助stackoverflow.com/questions/9215820/…

标签: java unique bigdata


【解决方案1】:

您可以先尝试查找重复的行哈希以识别潜在的重复行:

Map<Integer, Integer> hashes = new HashMap<> ();
Map<Integer, Integer> dupes = new HashMap<> ();
int i = 0;
while ((line = buff.readLine()) != null) {
  int hash = line.hashCode();
  Integer previous = hashes.get(hash);
  if (previous != null) { //potential duplicate
    dupes.put(i, previous);
  } else {
    hashes.put(hash, i);
  }
  ++i;
}

最后你有一个潜在重复的列表。如果dupes 为空,则没有重复,如果不是,则可以对文件进行第二次传递以检查行是否真的相同。

【讨论】:

  • 使用 Koloboke IntIntMap 或 Trove TIntIntHashMap 来表示地图也会更节省内存。
  • 关于第二遍,如果您切换到启用随机访问的文件,则可以跳过它。然后,您可以向后滚动并检查每个潜在重复项上的行。实际上,无论如何,如果没有随机访问,第二遍是不可能的。
  • @bezmax 随机访问无法帮助您转到第 xyz 行 - 您只能跳过一些字节 - 在我的示例中,我可以存储字节位置而不是行号。
  • @assylias 这就是我的暗示。我不相信有一种方法可以使它与您的示例中的行号一起使用。在第二遍期间,您必须比较它们之间的重复项,因此您需要将它们全部加载到内存或使用随机访问。加载到内存也不起作用 - 对于最坏的情况(所有行重复),您需要将完整文件加载到内存。
【解决方案2】:

由于您的 RAM 内存,您不能以这种方式执行该操作。相反,您可以读取文件并生成 n 个具有固定大小(例如:10.000 行)的文件,读取一行并将其放入实际文件中。当您达到文件限制时,打开一个新文件并释放所有对象以节省内存,然后进行第二次循环并使用字符串(针对该行)将原始文件的每一行与生成的 n 个文件进行比较。或许这样可以避免内存缺口。

有点奇怪,过程会很慢,但这样我认为你可以达到你的要求。

如果您需要代码,请告诉我。

希望有帮助

【讨论】:

  • 您知道您不需要将整个文件加载到内存中来处理它,不是吗?
【解决方案3】:

你可以这样作弊:(例如 Groovy,但等效的 Java 也可以)

def hashes = []
def writer = new PrintWriter(new FileWriter("out.txt"))
new File('test.txt').eachLine { line ->
    def hashCode = DigestUtils.sha256Hex(line) //Commons digest library
    if (!(hashCode in hashes)) {
        hashes << hashCode
        writer.println(line)
    }
}
writer.close()

运行所需的 RAM 不应该超过 1GB。与标准的 hashCode 方法相比,SHA256 哈希值可能会让您更加确定行的唯一性。

【讨论】:

  • 它应该检测哈希冲突,因为他想要一个只有唯一行的文件。让我猜猜,你是那个投反对票的人,不是你......
猜你喜欢
  • 2010-09-22
  • 2019-07-28
  • 2012-07-21
  • 2013-11-12
  • 2012-01-05
  • 1970-01-01
  • 2010-09-11
  • 2019-05-17
  • 2021-10-03
相关资源
最近更新 更多