【问题标题】:Consume Split Partition List in C#在 C# 中使用拆分分区列表
【发布时间】:2020-02-28 04:04:58
【问题描述】:

我如何使用这种拆分列表的方法

private List<List<T>> SplitPartition<T>(this IEnumerable<T> collection, int size)
{
    var chunks = new List<List<T>>();
    var count = 0;
    var temp = new List<T>();

    foreach (var element in collection)
    {
        if (count++ == size)
        {
            chunks.Add(temp);
            temp = new List<T>();
            count = 1;
        }
        temp.Add(element);
    }
    chunks.Add(temp);

    return chunks;
}

我想在我现有的代码中实现这个列表分区:

public void ExportToCsv()
{
    List<GenerateModel> members = getDataTop5000(); // I got data from my List of Data with return List<>

    int offset = 0;
    const int numberPerBatch = 500000; // count parameter.
    double LoopMax = Math.Ceiling(members.Count / (double)numberPerBatch);

    var PartitionMembers = members.SplitPartition(numberPerBatch); //error here

    while (offset < LoopMax)
    {
        //do the index of partion here  PartitionMembers

        offset++;
    }
}

任何建议或示例如何使用这些方法?这真的是我需要分区到我的列表。当我尝试使用该方法时,出现如下错误:

List' 不包含“SplitPartition”的定义,并且找不到接受“List”类型的第一个参数的可访问扩展方法“SplitPartition”(您是否缺少 using 指令或程序集引用?)

【问题讨论】:

  • 哎呀,这可能会很糟糕。此外,切勿在不包含确切错误消息的情况下在 SO 上发帖说“我遇到错误”
  • @CaiusJard 抱歉,我将添加我的错误消息,我将编辑我的问题
  • Numberperbatch 应声明为 int。发布您的错误消息。
  • 嗨,extension method (SplitPartition) 是private。您在哪里尝试调用它?是同一个班吗?
  • @CaiusJard:你为什么说表演会很“糟糕”?虽然您可以通过设置其中一些列表的 Capacity 来轻松提升性能,但它仍然是 O(n)。

标签: c# arraylist ienumerable


【解决方案1】:

我认为你最好推出自己的解决方案。假设您已经下载了 5000 个成员并希望将它们写入 50 个成员块(100 个文件)中的文件,您可以这样做:

StringBuilder sb = new StringBuilder(10000);
int x= 0;
foreach(var m in members){
  if(++x%50 == 0){
    File.WriteAllText(sb.ToString(), $@"c:\temp\{x%50}.csv");
    sb.Length = 0;
  }
  sb.AppendLine(m.ToCsvRepresentationEtc());
}

我要说的不是写入文件,而是要知道你想用你的块做什么(例如写入文件)并单次传递可枚举并切割成块不时改变你采取的行动。在此示例中,更改操作是一个简单的模数,它清空 StringBuilder 的缓冲区并根据模数写入文件名。这比在预分块时消耗大量内存更可取(拆分例程的性能可能很可怕,具体取决于所涉及的数字;它不会尝试根据数字提供任何适当大小的列表)

至少考虑重写分块,以便它使用直接的 2d(锯齿状)数组或预先提供容量的列表;您可以从传入的 List 的大小和块大小知道它们需要的大小:


public static class ListExtensions{

  public List<List<T>> SplitPartition<T>(this IEnumerable<T> collection, int size)
  {
    var chunks = new List<List<T>>(collection.Count/size + 1);
    var temp = new List<T>(size);

    foreach (var element in collection)
    {
        if (temp.Count == size)
        {
            chunks.Add(temp);
            temp = new List<T>(size);
        }
        temp.Add(element);
    }
    chunks.Add(temp);

    return chunks;
  }
}

【讨论】:

  • 感谢您的建议答案,但最后我更改并将方法 SplitPartition 移动到另一个静态类。它奏效了。
  • 这就是我回答中最后一段代码的作用。它还简化了方法并使用数学来预先分配列表。如果您在创建列表时没有调整它的大小,那么列表的问题是它会在空间不足时调整大小,我的意思是它在内部分配了一个新数组,其大小是旧数组的两倍,并复制所有内容超过。 AFAICR 从 16 开始,因此如果您在其中加载 5000 个项目,它将在 16 32,64,128,256,512,1024,2048,4096 调整大小,最后是 8192 - 9 调整大小和数千次不必要的复制操作。如果你知道你想要 5000,请预先调整到 5000
  • PS 刚刚注意到您一次将分块成半百万行的评论。为此使用自动扩展列表非常可怕。我想我会接受我的第一个建议,即在每 50 万条记录上自动滚动 csv 文件路径,并将它们从数据库流式传输到文件,逐行写入。这是最小的内存使用。对 50 万条记录使用列表是 15 次调整大小和数百万次不必要的复制操作
  • 为什么我需要var chunks = new List&lt;List&lt;T&gt;&gt;(collection.Count/size + 1);?我已经按照此代码 double LoopMax = Math.Ceiling(members.Count / (double)numberPerBatch); 解释我来划分集合
  • 我在上面的两个cmets中详细解释了为什么? List 并不是什么能平稳增长的神奇东西,它有一个 16 的内部数组,当它填满时它会翻倍,但这意味着将所有项目从 16 数组复制到 32 数组。然后当 32 已满时,它会翻倍并再次复制。它不断地翻倍和复制,翻倍和复制,它真的很慢,而且不断地翻倍和复制会浪费大量资源,所以当你知道你要在列表中放入 5000 个项目时,你只需使用预先分配一个5000 个数组.. 不再加倍和复制
【解决方案2】:

看不到你的完整代码,但是:

  • 扩展方法需要属于static类,并且
  • 这个类和方法需要在调用代码中可见。

特别是,我可以看到您的ExportToCsv 不是静态的,因此它不属于静态类,因此我也可以推断出您的private 扩展方法:

  • 不属于静态类,或者
  • 与您的 ExportToCsv 方法属于一个单独的类,因此无法从中看到

因此,创建一个public static 类来保存扩展方法,将方法本身标记为public static,然后您就应该开始营业了。

更多详情:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/how-to-implement-and-call-a-custom-extension-method

【讨论】:

  • 问题是方法不能在同一个类中访问。 excatly SplitPartition 在静态方法中,ExportToCsv 消耗。我搬到另一个静态类,它解决了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-26
  • 2014-11-08
  • 1970-01-01
  • 2013-10-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多