c# - 异步修改相关对象列表会产生意外结果
问题描述
我有一个对象列表,它们之间可以有一个或多个关系。我想遍历这个列表并将每个对象与列表中的所有其他对象进行比较,在比较对象时设置关系。因为现实生活中的这种比较相当复杂且耗时,所以我尝试异步执行此操作。
我很快整理了一些示例代码,以相当简单的方式说明了手头的问题。
class Program
{
private static readonly Word[] _words =
{
new Word("Beef"),
new Word("Bull"),
new Word("Space")
};
static void Main()
{
var tasks = new List<Task>();
foreach (var word in _words)
{
tasks.Add(CheckRelationShipsAsnc(word));
}
Task.WhenAll(tasks);
}
static async Task CheckRelationShipsAsnc(Word leftWord)
{
await Task.Run(() =>
{
foreach (var rightWord in _words)
{
if(leftWord.Text.First() == rightWord.Text.First())
{
leftWord.RelationShips.Add(rightWord);
}
}
});
}
}
class Word
{
public string Text { get; }
public List<Word> RelationShips { get; } = new List<Word>();
public Word(string text)
{
if(string.IsNullOrEmpty(text)) throw new ArgumentException();
Text = text;
}
public override string ToString()
{
return $"{Text} ({RelationShips.Count} relationships)";
}
}
预期的结果是“Space”没有任何关系,而“Bull”和“Beef”这两个词彼此之间只有一种关系。我得到的是所有的词都没有关系。我无法理解到底是什么问题。
解决方案
您的算法具有O(n^2)
时间复杂度。如果您有大量要相互比较的项目,这将是一个问题。例如,如果您有 1000 个项目,这会给您 1000 * 1000 = 1000000(一百万)次比较。
考虑使用另一种方法。我不知道这是否适用于您的实际问题,但对于此示例,假设每个单词都以大写字母 A..Z 开头,您可以将相关单词按首字母存储在长度为 26 的单词数组中列表。
var a = new List<Word>[26];
// Initialize array with empty lists
for (int i = 0; i < a.Length; i++) {
a[i] = new List<Word>();
}
// Fill array with related words
foreach (var word in _words) {
a[word.Text[0] - 'A'].Add(word); // Subtracting 'A' yields a zero-based index.
}
请注意,您的原始解决方案有两个嵌套循环(其中一个隐藏在 的调用中CheckRelationShipsAsnc
)。这个解决方案只有一层循环,时间复杂度O(n)
达到这里。
现在,您可以在相应数组位置的一个列表中找到所有相关单词。获取此信息,您现在可以连接同一列表中的单词。这部分还在O(n^2)
;但是,这里n
要小得多,因为 is 仅指相关列表中的单词,而不是初始_words
数组的长度。
根据您的实际问题的表述方式,使用 aDictionary<char, List<Word>>
代替 my 数组可能会更好a
。数组解决方案需要一个索引。在现实世界的问题中,关系条件可能无法表述为指标。字典需要一个键,任何类型的对象都可以用作键。请参阅:的备注部分Dictionary<TKey,TValue> Class
。
以这种方式优化的算法可能比多任务解决方案更快。
推荐阅读
- unix - 我如何删除除unix中每8个字符之外的每个字符
- android - Flutter 在 ListView 中不显示有状态的子 Widget
- python - 如果满足某个条件,如何在 for 循环中停止产生 scrapy spider?
- blazor - Blazor Webassembly:错误 CS0131:赋值的左侧必须是变量、属性或索引器
- python - 关闭时似乎有 6 个泄漏的信号量对象需要清理 warnings.warn('resource_tracker: 似乎有 %d
- api - 计算 Steam 库存值
- integromat - Integromat Watch Google Contacts - 删除操作怎么办?
- android - 如何访问文件夹特定的应用程序android studio
- python - 无法 pip 安装 dlib
- python - Python/SQLAlchemy 中的部分 UUID 匹配