【问题标题】:Optimize Duplicate file entry Removal method优化重复文件条目删除方法
【发布时间】:2012-05-20 08:33:02
【问题描述】:

我有以下代码要优化。由于我希望文件很大,因此我没有使用 HashMap 来存储行,而是选择了 String 数组。我尝试用大约 500,000 的 n 测试逻辑,它运行了大约 14 分钟。我肯定想让它比这快得多,并感谢任何帮助或建议。

         public static void RemoveDuplicateEntriesinFile(string filepath)
        {
              if (filepath == null)
                    throw new ArgumentException("Please provide a valid FilePath");
              String[] lines = File.ReadAllLines(filepath);
              for (int i = 0; i < lines.Length; i++)
              {
                    for (int j = (i + 1); j < lines.Length; j++)
                    {
                          if ((lines[i] !=null) && (lines[j]!=null) && lines[i].Equals(lines[j]))
                          {//replace duplicates with null
                                lines[j] = null;
                          }
                    }
              }

              File.WriteAllLines(filepath, lines);
        }

提前致谢!

【问题讨论】:

  • Jon Skeet 已经回答了这个问题:stackoverflow.com/questions/1245500/…
  • 哇,在阅读了这个答案有多复杂之后,我不得不怀疑我的答案是否可行,它要简单得多。

标签: c# .net


【解决方案1】:

“因为我希望文件很大,所以我没有使用 HashMap 来存储行,而是选择了 String 数组。”

我不同意你的推理;文件越大,您从散列中获得的性能优势就越大。在您的代码中,您将每一行与 所有 后续行进行比较,整个文件的计算复杂度为 O(n²)。

另一方面,如果您要使用高效的散列算法,那么每次散列查找将在 O(1) 内完成;处理整个文件的计算复杂度变为 O(n)。

尝试使用HashSet&lt;string&gt; 并观察处理时间的差异:

public static void RemoveDuplicateEntriesinFile(string filepath)
{
    if (filepath == null)
        throw new ArgumentException("Please provide a valid FilePath");

    HashSet<string> hashSet = new HashSet<string>(File.ReadLines(filepath));
    File.WriteAllLines(filepath, hashSet);
}

编辑:您能否尝试以下版本的算法并检查需要多长时间?它经过优化以最大程度地减少内存消耗:

HashAlgorithm hashAlgorithm = new SHA256Managed();
HashSet<string> hashSet = new HashSet<string>();
string tempFilePath = filepath + ".tmp";

using (var fs = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write))
using (var sw = new StreamWriter(fs))
{
    foreach (string line in File.ReadLines(filepath))
    {
        byte[] lineBytes = Encoding.UTF8.GetBytes(line);
        byte[] hashBytes = hashAlgorithm.ComputeHash(lineBytes);
        string hash = Convert.ToBase64String(hashBytes);

        if (hashSet.Add(hash))
            sw.WriteLine(line);
    }
}

File.Delete(filepath);
File.Move(tempFilePath, filepath);

【讨论】:

  • 我目前正在运行代码以了解它比我的好多少。 :)
  • 事实证明上面的内容并不比我的好多少。我对其进行了测试,它以 Time Elapsed = 32.14.838245 返回。与我的原始代码大致相同。有什么想法可以让它更快吗?
  • 14 分钟无论如何都太长了。我刚刚在 500,000 个随机 GUID(每个 36 个字符)上测试了 HashSet&lt;string&gt; 的性能,不到一秒就完成了。您的问题可能是 I/O 之一。
  • 实际上每行的字符数是可变的。
  • @user272671:那么文件有多大(以字节为单位)?
【解决方案2】:

您可以尝试创建一个新列表并将其添加到其中。

        public static void RemoveDuplicateEntriesinFile(string filepath)
        {
              if (filepath == null)
                    throw new ArgumentException("Please provide a valid FilePath");
              String[] lines = File.ReadAllLines(filepath);
              List<String> newLines=new List<String>()
              foreach (string s in lines)
              {
                   if (newLines.Contains(s)
                   continue;
                   newLines.add(s);
              }
              //not sure if you can do this with a list, might have to convert back to array
              File.WriteAllLines(filepath, newLines);
        }

【讨论】:

  • 您将不得不运行测试以查看它是否真的被优化了,但我认为它会因为您将列表迭代到 2 的幂,这对于 500 000 行来说太疯狂了。
【解决方案3】:

lines[j] = null; 对我不起作用。 File.WriteAllLines(filepath, lines); 将这些行写为 "" (string.Empty)

【讨论】:

    猜你喜欢
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 2015-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多