依赖于未修改的哈希集的迭代顺序
本文关键字:顺序 迭代 哈希集 依赖于 未修改 | 更新日期: 2023-09-27 18:31:29
HashSet<object> myHashSet = new HashSet<object>();
// Iteration 1
foreach (object myObject in myHashSet) { ... }
// Some instructions THAT DO NOT MODIFY myHashSet
...
// Iteration 2
foreach (object myObject in myHashSet) { ... }
在两次迭代中,myHashSet
的对象是否会以相同的顺序枚举?
根据 HashSet
(link) 的参考源,在没有集合修改的情况下,迭代顺序是可预测的。
public bool MoveNext() {
if (version != set.m_version) {
throw new InvalidOperationException(SR.GetString(SR.InvalidOperation_EnumFailedVersion));
}
while (index < set.m_lastIndex) {
if (set.m_slots[index].hashCode >= 0) {
current = set.m_slots[index].value;
index++;
return true;
}
index++;
}
index = set.m_lastIndex + 1;
current = default(T);
return false;
}
但是,这不太可能在 .NET 平台的未来版本或其他实现中更改。要确保顺序保持不变,请在第一次迭代时从集合中创建一个列表,并将该列表用于第二次迭代:
var myList = myHashSet.ToList();
foreach( var obj myObject in myList) ...
// Some instructions (may or may not modify myHashSet, it no longer matters)
foreach( var obj myObject in myList) ...
这有两个可能的答案,真的。
第一个是"是的,因为枚举HashSet
是确定性的,如果你不相信我,你可以简单地查看源代码"。
第二个是"不,因为如果你想获得技术,HashSet<T>.GetEnumerator()
的文档并没有说顺序是确定性的,所以明天实现可能会改变"。
使用第一个答案来完成工作不会错。如果你想绝对地确定,你当然可以使用myHashSet.ToArray()
并只迭代它,但仅仅为了技术性而引入副本很可能是矫枉过正的。在这种情况下,实现必须不遗余力地实现非确定性的枚举。
不过,一个重要的警告是:强调HashSet
在枚举之间不会修改并没有错,因为实现在集合更改时更改顺序是合理的(这取决于项在内部的存储方式)。