【问题标题】:How to efficiently filter objects out of an (initially) large list of objects如何有效地从(最初)大型对象列表中过滤对象
【发布时间】:2016-01-29 18:51:14
【问题描述】:

我需要将大量复杂(20 多个属性)对象过滤到多个子列表中。要创建子列表,我有一个过滤器规范列表。要求是:a) 一个项目不允许成为两个子列表的一部分,并且 b) 必须能够在处理完成后获取所有未划分的项目。

目前我使用以下算法:

  1. 列表项
  2. 将要过滤的对象放入通用列表中
  3. 对于每个过滤器规格:
    • 创建 Where 表达式 (Expression>)
    • 使用 Linq > Where 将表达式应用于对象列表
    • 获取选定对象的结果 IEnumerable 并将它们与过滤器的描述一起存储在列表中
    • 使用 Linq 从源列表中删除找到的项目 > 除了创建一个新列表以继续使用并防止将对象放入多个子列表中
  4. 检查工作列表中是否还有(未分割的)对象

我的初始对象列表可能超过 400.000 个对象,我注意到过滤和减少工作列表都需要一些时间。所以我想知道:

  1. 创建子列表的过滤发生在我的对象的最多 7 个属性上。有没有办法提高 Linq > Where 选择的性能?
  2. 有没有办法防止项目被选择到多个子列表中,而不会通过使用 except 或 RemoveAll 减少工作集合(可能的改进)?

提前致谢!

【问题讨论】:

  • 这完全取决于对象列表是否按某些过滤属性排序或索引。如果不是,那么仅手动遍历列表一次 (foreach) 并使用 if elseif else 语句按顺序对项目进行分类可能会快得多
  • 如何在 Linq 可以使用的对象集合上创建索引?此外,现实生活中的情况比我上面的描述更复杂。在现实生活中,不仅有一个过滤器描述列表,还有一个类别的树结构,每个类别都有自己的过滤器规范和(最终)符合过滤器规范的对象的子列表。跨度>
  • 这就是我的意思。如果列表没有以任何方式索引,那么使用 LinqWhere 扩展方法基本上无论如何都会迭代整个序列。在这种情况下,您最好只迭代一次,并在进行过程中对每个项目进行分类,从而避免昂贵的 removeexcept 操作一起
  • 对它们进行分类将是一个挑战,因为过滤器不仅包含相等测试,还包含大于、小于等测试,并且属性值不限于两个或三个值,理论上会产生比我必须过滤的对象数量更多的独特组合。
  • 我不理解你。您有一个有效的算法,但您想提高性能。如果它有效,那么您就有办法在 Where 子句中构建所有谓词。是什么阻止您手动执行此操作?请看下面我的回答。也许我在这里误解了一些东西。

标签: c# performance linq list large-data-volumes


【解决方案1】:

如果您无法利用您尝试分类的传入列表中的任何索引,那么您最好只遍历整个列表一次并在进行时对项目进行分类。这样可以避免不必要的 removeexcept 操作,这些操作会通过无意义的迭代和相等比较严重损害性能。

我在想一些很长的事情:

public static IDictionary<string, List<T>> Classify<T>(this IEnumerable<T> items, IDictionary<string, Predicate<T>> predicates, out List<T> defaultBucket)
{
    var classifiedItems = new Dictionary<string, List<T>>(predicates.Count);
    defaultBucket = new List<T>();

    foreach (var predicate in predicates)
    {
        classifiedItems.Add(predicate.Key, new List<T>()); 
    }

    foreach (var item in items)
    {
        var matched = false;

        foreach (var predicate in predicates)
        {
            if (predicate.Value(item))
            {
                matched = true;
                classifiedItems[predicate.Key].Add(item);
                break;
            }
        }

        if (!matched)
        {
            defaultBucket.Add(item);
        }
    }

    return classifiedItems;
}

任何给定的predicate 都可以像您需要的那样复杂。唯一的条件是它接受T 并返回bool。如果这还不够,没有什么能阻止您使用您需要的任何签名来实现您自己的MyPredicate&lt;???&gt;

EDIT:编辑代码以处理“默认存储桶”,其中不符合任何指定谓词的项目。

【讨论】:

  • 我觉得完全一样。典型的分区算法。 +1
  • 现在我明白你的意思了。这确实是我正在寻找的答案类型。当然我可以遍历我的类别树。首先收集所有谓词。我认为上面的例子应该有一个补充,那就是制作一组不适用于任何谓词的项目。
  • 是的,我没有考虑任何不符合任何指定条件的项目。尽管如此,这还是很容易解决的。
  • @BjörnBoxstart 添加了我如何处理“默认存储桶”的情况。
  • 您在每个失败的谓词之后添加到defaultBucket,而不是在所有谓词都失败时添加。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-11
  • 2017-06-14
  • 2021-09-14
  • 1970-01-01
  • 2022-01-12
  • 1970-01-01
相关资源
最近更新 更多