更新大型列表时OutOfMemoryException?

时间:2014-07-14 14:37:18

标签: c# linq

我有一个很大的列表,如果需要,我想覆盖一个值。为此,我创建了列表的两个子集,似乎给了我OutOfMemoryException。这是我的代码片段:

if (ownRG != "")
{
    List<string> maclist = ownRG.Split(',').ToList();
    List<IVFile> temp = powlist.Where(a => maclist.Contains(a.Machine)).ToList();
    powlist = powlist.Where(a => !maclist.Contains(a.Machine)).ToList(); // OOME Here
    temp.ForEach(a => { a.ReportingGroup = ownRG; });
    powlist.AddRange(temp);
} 

基本上我将列表分成需要更新的部分和不需要更新的部分,然后我执行更新并将列表重新组合在一起。这适用于较小的列表,但在OutOfMemoryException的第三行中为if打破一个大列表。我可以提高效率吗?

注意
powlist是大型列表(&gt; 1m)。 maclist只有1到10之间,但即使有1个项目也会中断。

4 个答案:

答案 0 :(得分:2)

解决您的问题

以下是如何使用我的答案中的枚举器代码重新排列代码:

if (!string.IsNullOrEmpty(ownRG))
{
    var maclist = new CommaSeparatedStringEnumerable(str);
    var temp = powlist.Where(a => maclist.Contains(a.Machine));

    foreach (var p in temp)
    {
        p.ReportingGroup = ownRG;
    }
} 
  • 您不应在代码中使用ToList
  • 您无需从temp中移除powlist的内容(无论如何,您正在重新添加它们)

以逗号分隔的大字符串

进行流式传输

您可以通过查找,字符并记住上次找到的字符和之前的字符的位置,手动迭代列表而不是执行您现在所做的操作。这肯定会使你的应用程序工作,因为它不需要立即将整个集合存储在内存中。

代码示例:

var str = "aaa,bbb,ccc";
var previousComma = -1;
var currentComma = 0;

for (; (currentComma = str.IndexOf(',', previousComma + 1)) != -1; previousComma = currentComma)
{
    var currentItem = str.Substring(previousComma + 1, currentComma - previousComma - 1);
    Console.WriteLine(currentItem);
}
var lastItem = str.Substring(previousComma + 1);
Console.WriteLine(lastItem);

自定义迭代器

如果你想以一种奇特的方式'正确',你甚至可以写一个自定义的枚举器:

public class CommaSeparatedStringEnumerator : IEnumerator<string>
{
    int previousComma = -1;
    int currentComma = -1;
    string bigString = null;
    bool atEnd = false;

    public CommaSeparatedStringEnumerator(string s)
    {
        if (s == null)
            throw new ArgumentNullException("s");

        bigString = s;
        this.Reset();
    }

    public string Current { get; private set; }

    public void Dispose() { /* No need to do anything here */ }

    object IEnumerator.Current { get { return this.Current; } }

    public bool MoveNext()
    {
        if (atEnd)
            return false;

        atEnd = (currentComma = bigString.IndexOf(',', previousComma + 1)) == -1;

        if (!atEnd)
            Current = bigString.Substring(previousComma + 1, currentComma - previousComma - 1);
        else
            Current = bigString.Substring(previousComma + 1);

        previousComma = currentComma;
        return true;
    }

    public void Reset()
    {
        previousComma = -1;
        currentComma = -1;
        atEnd = false;
        this.Current = null;
    }
}

public class CommaSeparatedStringEnumerable : IEnumerable<string>
{
    string bigString = null;

    public CommaSeparatedStringEnumerable(string s)
    {
        if (s == null)
            throw new ArgumentNullException("s");

        bigString = s;
    }

    public IEnumerator<string> GetEnumerator()
    {
        return new CommaSeparatedStringEnumerator(bigString);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

然后你可以像这样迭代它:

var str = "aaa,bbb,ccc";
var enumerable = new CommaSeparatedStringEnumerable(str);

foreach (var item in enumerable)
{
    Console.WriteLine(item);
}

其他想法

  

我可以提高效率吗?

是的,你可以。我建议使用更高效的数据格式(您可以根据需要查看数据库或XML,JSON等)。如果您真的想使用以逗号分隔的项目,请参阅上面的代码示例。

答案 1 :(得分:1)

在循环中找到下一个','char。取','和前一个','位置之间的子串。在循环结束时保存对前一个','位置的引用(最初设置为0)。因此,您逐个解析项目而不是一次解析所有项目。

答案 2 :(得分:1)

无需从powlist创建一堆子列表并重新构建它。只需循环遍历powlist并相应地更新ReportingGroup属性。

var maclist = new HashSet<string>( ownRG.Split(',') );
foreach( var item in powlist) {
    if( maclist.Contains( item.Machine ) ){
        item.ReportingGroup = ownRG;
    }
}

由于这会更改powlist,因此您不会分配任何额外的内存,也不应该遇到OutOfMemoryException

答案 3 :(得分:-2)

您可以尝试循环列表中的项目,但这会增加处理时间。

foreach(var item in powlist)
{
//do your opeartions
}