c# - 树木修剪性能不佳

标签 c# linq tree breadth-first-search

我制作了我称之为 TreePruner 的东西。其目的:给定一个从根级节点列表开始的层次结构,返回一个新的层次结构,其中新的根节点是满足特定条件的最高级别节点。这是我的课。

public class BreadthFirstPruner<TResource>
{
    private IEnumerable<TResource> originalList;
    private IEnumerable<TResource> prunedList;
    private Func<TResource, ICollection<TResource>> getChildren;

    public BreadthFirstPruner(IEnumerable<TResource> list, Func<TResource, ICollection<TResource>> getChildren)
    {
        this.originalList = list;
        this.getChildren = getChildren;
    }

    public IEnumerable<TResource> GetPrunedTree(Func<TResource,bool> condition)
    {
        this.prunedList = new List<TResource>();
        this.Prune(this.originalList, condition);
        return this.prunedList;
    }

    private void Prune(IEnumerable<TResource> list, Func<TResource,bool> condition)
    {
        if (list.Count() == 0)
        {
            return;
        }

        var included = list.Where(condition);
        this.prunedList = this.prunedList.Union(included);
        var excluded = list.Except(included);
        this.Prune(excluded.SelectMany(this.getChildren), condition);
    }
}

类(class)按预期进行,但速度很慢,我不明白为什么。我已经在非常小的层次结构上使用了它,其中完整的层次结构已经在内存中(因此应该没有 linq-to-sql 的意外)。但无论我尝试做事时多么急切或懒惰,实际评估 linq 表达式结果的第一行代码最终需要 3-4 秒才能执行。

这是当前使用修剪器的代码:

Func<BusinessUnitLabel, ICollection<BusinessUnitLabel>> getChildren = l => l.Children;
var hierarchy = scope.ToList();
var pruner = new BreadthFirstPruner<BusinessUnitLabel>(hierarchy, getChildren);
Func<BusinessUnitLabel, bool> hasBusinessUnitsForUser = l =>
    l.BusinessUnits.SelectMany(bu => bu.Users.Select(u => u.IDGUID)).Contains(userId);
var labels = pruner.GetPrunedTree(hasBusinessUnitsForUser).ToList();

如前所述,执行此操作时我使用的数据集非常小。它只有几层深,大多数层上只有一个节点。正如目前所写的那样,当我调用 list.Count() 时,第一次递归调用 Prune 时会出现缓慢,因为那是正在评估层次结构的第二级 (excluded.SelectMany(this.getChildren))。

但是,如果我像这样添加一个 .ToList 调用:

var included = list.Where(condition).ToList()

那么慢就会发生在那个点

我需要做什么才能使这件事进展顺利?

更新

在有人提示我更仔细地重新评估我的情况后,我意识到 hasBusinessUnitsForUser 中的那些关联并没有被预先加载。那就是问题所在。

最佳答案

这些调用都是延迟执行的,结果没有缓存/具体化:

    var included = list.Where(condition);
    this.prunedList = this.prunedList.Union(included);
    var excluded = list.Except(included);

即使在这段代码中,included 也会运行两次。由于这是一个递归算法,因此可能会有更多的调用。

ToList 调用添加到任何可能被执行多次的序列。

关于c# - 树木修剪性能不佳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32853202/

相关文章:

c# - C++/命令行界面 : Implementing an abstract C# class that implements INotifyPropertyChanged results in C++ compiler error C3766

c# - 使用 LINQ 过滤集合

javascript - D3JS闪烁链接

从树中删除节点时的java内存问题

c - 如何在工作流树中表示 fork() && fork()?

c# - C#中如何从DataGridView中获取选中的行内容?

javascript - 未捕获的类型错误 : Cannot read property 'msie' of undefined

c# - 简化正则表达式或模式

c# - Mongo C# Driver 2.0 聚合组异常

C# Linq 聚合中间值