List<T> 是不是保证插入顺序?
Posted
技术标签:
【中文标题】List<T> 是不是保证插入顺序?【英文标题】:Does List<T> guarantee insertion order?List<T> 是否保证插入顺序? 【发布时间】:2010-11-05 19:05:07 【问题描述】:假设我在一个列表中有 3 个字符串(例如“1”、“2”、“3”)。
然后我想重新排列它们以将“2”放在位置 1(例如“2”、“1”、“3”)。
我正在使用此代码(将 indexToMoveTo 设置为 1):
listInstance.Remove(itemToMove);
listInstance.Insert(indexToMoveTo, itemToMove);
这似乎有效,但我偶尔会得到奇怪的结果;有时订单不正确或列表中的项目被删除!
有什么想法吗? List<T>
保证订单吗?
相关:
Does a List<T> guarantee that items will be returned in the order they were added?
【问题讨论】:
这里提到的不是这样:***.com/a/1790318/696517 【参考方案1】:List<>
类确实保证了排序 - 事物将按照您添加它们的顺序保留在列表中,包括重复项,除非您明确对列表进行排序。
根据 MSDN:
...List "表示一个强类型的对象列表,可以 按索引访问。”
索引值必须保持可靠才能准确。因此,订单是有保证的。
如果您将项目移到列表的后面,您的代码可能会得到奇怪的结果,因为您的 Remove()
将在调用 Insert()
之前将所有其他项目移到一个位置。
你能把你的代码简化成小到可以发布的东西吗?
【讨论】:
对于任何未来的 googler,这里是来自 MSDN(粗体我的)List(T).Add 的确切引用 要添加到列表 end 的对象。对于引用类型,该值可以为 null。 我们是否可以从 Microsoft 或 C# 规范中获得更明确的引用/参考? @aolszowka 的引用肯定似乎表明它确实保留了插入顺序,但从技术上讲,列表可以在添加项目后的任何时候重新排序集合,并且该语句仍然有效。我不想对此挑剔,但如果经理或 QA 真的让我捍卫这个职位,我不会对这句话感到非常自信。 我可以想到两种有用的方法来获得一些确认。首先,read the source 并满足自己。其次,在任何好的计算机科学教科书中查看抽象数据类型List
的定义。就像Queue
和Stack
一样,List
是一个定义明确、可预测且易于理解的数据结构 - 如果 .NET 实现不同(或者如果它发生变化),很多软件会休息。
@tehDorf 我对此的看法(如果要对此进行讨论)是:“如果排序影响您的方法/算法的操作,您应该明确排序列表或让您的 API 采用适当的接口/类,例如 IOrderedEnumerable 或 SortedList,否则您不应依赖特定的实现以某种方式运行,除非明确明确说明“您还必须小心明确排序 ListWhere()
方法过滤列表时,您依赖于实现来按顺序考虑序列中的项目。碰巧确实如此(请参阅referencesource.microsoft.com/System.Core/System/Linq/…),但这在 MSDN 中没有记录。我建议使用emp.LastOrDefault(x => x.empDept = 'abc')
,因为LastOrDefault()
的文档确实表明它维护了项目的顺序。【参考方案2】:
正如 Bevan 所说,但请记住,列表索引是从 0 开始的。如果要将元素移动到列表的前面,则必须将其插入索引 0(而不是示例中所示的 1)。
【讨论】:
【参考方案3】:这是我将一个项目在列表中向下移动一个位置的代码:
if (this.folderImages.SelectedIndex > -1 && this.folderImages.SelectedIndex < this.folderImages.Items.Count - 1)
string imageName = this.folderImages.SelectedItem as string;
int index = this.folderImages.SelectedIndex;
this.folderImages.Items.RemoveAt(index);
this.folderImages.Items.Insert(index + 1, imageName);
this.folderImages.SelectedIndex = index + 1;
这用于将它向上移动一个位置:
if (this.folderImages.SelectedIndex > 0)
string imageName = this.folderImages.SelectedItem as string;
int index = this.folderImages.SelectedIndex;
this.folderImages.Items.RemoveAt(index);
this.folderImages.Items.Insert(index - 1, imageName);
this.folderImages.SelectedIndex = index - 1;
folderImages
当然是ListBox
,所以列表是ListBox.ObjectCollection
,而不是List<T>
,但它确实继承自IList
,所以它的行为应该相同。这有帮助吗?
当然,前者仅在所选项目不是列表中的最后一项时才有效,而后者仅在所选项目不是第一项时有效。
【讨论】:
【参考方案4】:这里有 4 个项目,以及它们的索引
0 1 2 3
K C A E
你想将 K 移动到 A 和 E 之间——你可能会想到位置 3。你在这里要小心你的索引,因为在删除之后,所有索引都会更新。
所以你先删除第 0 项,然后离开
0 1 2
C A E
然后你在 3 处插入
0 1 2 3
C A E K
要获得正确的结果,您应该使用索引 2。为了使事情保持一致,您需要发送到 (indexToMoveTo-1) if indexToMoveTo > indexToMove
,例如
bool moveUp = (listInstance.IndexOf(itemToMoveTo) > indexToMove);
listInstance.Remove(itemToMove);
listInstance.Insert(indexToMoveTo, moveUp ? (itemToMoveTo - 1) : itemToMoveTo);
这可能与您的问题有关。请注意我的代码未经测试!
编辑:或者,如果这适用于您的情况,您可以使用自定义比较器 (IComparer
) Sort
。
【讨论】:
没有仔细阅读 Bevan 的回复,我刚刚覆盖了他的基础。 是的,但是您已经详细说明并给出了 Bevan 答案的示例以及一些解决方案代码,因此您的答案不是同义词,我已投票支持您。【参考方案5】:如果你改变操作的顺序,你会避免奇怪的行为: 首先将值插入列表中的正确位置,然后将其从他的第一个位置删除。确保你按他的索引删除它,因为如果你按引用删除它,你可能会同时删除它们......
【讨论】:
以上是关于List<T> 是不是保证插入顺序?的主要内容,如果未能解决你的问题,请参考以下文章