繁体   English   中英

如何使用C#提高循环性能

[英]How to increase perfomance for loop using c#

我使用嵌套的for循环比较来自Microsoft项目的任务数据。 但是,由于该项目有很多记录(超过1000条),所以它非常缓慢。

如何提高性能?

for (int n = 1; n < thisProject.Tasks.Count; n++) 
{
    string abc = thisProject.Tasks[n].Name;
    string def = thisProject.Tasks[n].ResourceNames;
    for (int l = thisProject.Tasks.Count; l > n; l--) 
    {
        // MessageBox.Show(thisProject.Tasks[l].Name);
        if (abc == thisProject.Tasks[l].Name && def == thisProject.Tasks[l].ResourceNames) 
        {
            thisProject.Tasks[l].Delete();
        }
    }
}

如您Task.Delete ,我正在比较单个TaskNameResourceNames ,当我找到一个重复项时,我调用Task.Delete除去重复项

在这种情况下,哈希检查应该比嵌套循环要快得多,即O(n)vs O(n ^ 2)

首先,提供您自己的平等比较器

class TaskComparer : IEqualityComparer<Task> {
    public bool Equals(Task x, Task y) {
        if (ReferenceEquals(x, y)) return true;
        if (ReferenceEquals(x, null)) return false;
        if (ReferenceEquals(y, null)) return false;
        if (x.GetType() != y.GetType()) return false;
        return string.Equals(x.Name, y.Name) && string.Equals(x.ResourceNames, y.ResourceNames);
    }

    public int GetHashCode(Task task) {
        unchecked {
            return 
                ((task?.Name?.GetHashCode()         ?? 0) * 397) ^ 
                 (task?.ResourceNames?.GetHashCode() ?? 0);
        }
    }
}

不必太担心GetHashCode函数的实现。 这只是一个小菜板代码,由其属性组成唯一的哈希代码

现在您有了用于比较和哈希处理的此类,您可以使用以下代码删除重复项

var set = new HashSet<Task>(new TaskComparer());
for (int i = thisProject.Tasks.Count - 1; i >= 0; --i) {
    if (!set.Add(thisProject.Tasks[i]))
        thisProject.Tasks[i].Delete();
}

如您所见,您只需扫描所有元素,然后将它们存储到HashSet HashSet将根据我们的相等比较器检查所提供的元素是否重复。

现在,由于要删除它,因此将删除检测到的重复。 您可以通过将条件反转为if (set.Add(thisProject.Tasks[i]))并在其中处理if来修改此代码以仅提取Unique项目而不是删除Unique项。

Microsoft Project具有使此问题的简单工作的Sort方法。 按名称,资源名称和唯一ID对任务进行排序,然后循环比较相邻任务并删除重复项。 通过使用唯一ID作为第三个排序键,您可以确保删除以后添加的重复项。 或者,您可以使用任务ID删除计划中较低级别的任务。 这是有关如何执行此操作的VBA示例:

Sub RemoveDuplicateTasks()

    Dim proj As Project
    Set proj = ActiveProject

    Application.Sort Key1:="Name", Ascending1:=True, Key2:="Resource Names", Ascending2:=True, Key3:="Unique ID", Ascending3:=True, Renumber:=False, Outline:=False
    Application.SelectAll
    Dim tsks As Tasks
    Set tsks = Application.ActiveSelection.Tasks

    Dim i As Integer
    Do While i < tsks.Count
        If tsks(i).Name = tsks(i + 1).Name And tsks(i).ResourceNames = tsks(i + 1).ResourceNames Then
            tsks(i + 1).Delete
        Else
            i = i + 1
        End If
    Loop

    Application.Sort Key1:="ID", Renumber:=False, Outline:=False
    Application.SelectBeginning

End Sub

注意:这个问题与算法有关,与语法无关; VBA易于转换为c#。

这应该为您提供所有重复的项目,因此您可以从原始列表中将其删除。

thisProject.Tasks.GroupBy(x => new { x.Name, x.ResourceNames}).Where(g => g.Count() > 1).SelectMany(g => g.Select(c => c));

请注意,您可能不希望删除所有它们,而只是删除重复的版本,因此请谨慎使用此列表。

从任务列表中获取不同元素的一种Linq方法:

public class Task
{
    public string Name {get;set;}
    public string ResourceName {get;set;}
}

public class Program
{
    public static void Main()
    {
        List<Task> Tasks = new List<Task>();
        Tasks.Add(new Task(){Name = "a",ResourceName = "ra"});
        Tasks.Add(new Task(){Name = "b",ResourceName = "rb"});
        Tasks.Add(new Task(){Name = "c",ResourceName = "rc"});
        Tasks.Add(new Task(){Name = "a",ResourceName = "ra"});
        Tasks.Add(new Task(){Name = "b",ResourceName = "rb"});
        Tasks.Add(new Task(){Name = "c",ResourceName = "rc"});

        Console.WriteLine("Initial List :");
        foreach(var t in Tasks){
            Console.WriteLine(t.Name);  
        }

        // Here comes the interesting part
        List<Task> Tasks2 = Tasks.GroupBy(x => new {x.Name, x.ResourceName})
                                 .Select(g => g.First()).ToList();

        Console.WriteLine("Final List :");
        foreach(Task t in Tasks2){
            Console.WriteLine(t.Name);  
        }
    }
}

这将选择每个具有相同NameResourceName第一个元素。

此处运行示例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM