更新大列表时抛出OutOfMemoryException异常

本文关键字:OutOfMemoryException 异常 列表 更新 | 更新日期: 2023-09-27 18:12:54

我有一个大列表,如果需要,我想覆盖一个值。为了做到这一点,我创建了列表的两个子集,这似乎给了我一个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);
} 

本质上,我将列表分成需要更新的部分和不需要更新的部分,然后执行更新并将列表重新组合在一起。这对于较小的列表很好,但是对于较大的列表,在if的第三行上出现OutOfMemoryException就会中断。我能让它更有效率吗?

注意
powlist为大列表(>1m)项。maclist只有1到10项,但即使只有1项,这也打破了。

更新大列表时抛出OutOfMemoryException异常

解决您的问题

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

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
  • 你不需要从powlist中删除temp的三个内容(无论如何你都要重新添加它们)

流式传输一个大的逗号分隔字符串

您可以手动遍历列表,而不是做您现在做的事情,通过查找,字符并记住最后找到的字符和前一个字符的位置。这肯定会使你的应用程序工作,因为这样它就不需要一次将整个集合存储在内存中。

代码示例:

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等)。如果您真的想使用逗号分隔的项,请参阅上面的代码示例。

没有必要从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

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

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

foreach(var item in powlist)
{
//do your opeartions
}
相关文章:
  • 没有找到相关文章