如何使用 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
上的 Name
和 ResourceNames
,当我找到重复项时,我调用 Task.Delete
来摆脱重复项
这应该会为您提供所有重复项,因此您可以将它们从原始列表中删除。
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);
}
}
}
这将选择具有相同 Name
和 ResourceName
的每个第一个元素。
运行 示例 here.
在这种情况下,哈希检查应该比嵌套循环快得多,即 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
函数的实现;这只是一个烤盘代码,它根据其属性组成一个独特的哈希码
现在你有了这个 class 用于比较和散列,你可以使用下面的代码来删除你的骗子
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
项而不是删除重复项
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#。
我使用嵌套 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
上的 Name
和 ResourceNames
,当我找到重复项时,我调用 Task.Delete
来摆脱重复项
这应该会为您提供所有重复项,因此您可以将它们从原始列表中删除。
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);
}
}
}
这将选择具有相同 Name
和 ResourceName
的每个第一个元素。
运行 示例 here.
在这种情况下,哈希检查应该比嵌套循环快得多,即 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
函数的实现;这只是一个烤盘代码,它根据其属性组成一个独特的哈希码
现在你有了这个 class 用于比较和散列,你可以使用下面的代码来删除你的骗子
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
项而不是删除重复项
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#。