List <T>是否保证插入顺序?


238

假设我在列表中有3个字符串(例如“ 1”,“ 2”,“ 3”)。

然后,我想对它们进行重新排序以将“ 2”放在位置1(例如“ 2”,“ 1”,“ 3”)。

我正在使用此代码(将indexToMoveTo设置为1):

listInstance.Remove(itemToMove);
listInstance.Insert(indexToMoveTo, itemToMove);

这似乎可行,但是我偶尔会得到奇怪的结果。有时订单不正确或列表中的项目被删除!

有任何想法吗?是否List<T>保证订单?

有关:

List <T>是否保证按添加顺序返回项目?


3
如此处所述,这是不正确的:stackoverflow.com/a/1790318/696517
jrmgx

Answers:


311

List<>类不保证排序-事情会在包括重复添加它们,顺序列表被保留,除非你明确地分类列表。

根据MSDN:

...列表“表示可以由index访问的对象的强类型列表。”

索引值必须保持可靠才能使其准确。因此,保证了订单。

如果您将代码移到列表的后面,则可能会从代码中得到奇怪的结果,因为您Remove()将在调用之前将所有其他项目下移一个位置Insert()

您可以将代码简化为足以发布的代码吗?


63
对于任何将来的googler,这里是MSDN(螺栓矿)中List(T).Add 的确切报价。要添加到List <T> 末尾的对象。对于引用类型,该值可以为null。
aolszowka,2011年

4
我们是否可以从Microsoft或C#规范中获得更明确的引用/参考?@aolszowka的引用似乎似乎暗示它确实保留了插入顺序,但是从技术上讲,列表可以在添加项目后的任何时间重新排序集合,并且该语句仍然有效。我不想对此挑剔,但是如果经理或质量检查人员确实让我捍卫了这个职位,那么仅凭那句话我就不会感到非常自信。
tehDorf 2015年

4
我可以想到两种有用的方法来获得确认。首先,阅读源代码并让自己满意。其次,List在任何好的计算机科学教科书中检查抽象数据类型的定义。就像Queue和一样Stack,a List是一个定义明确,可预测且易于理解的数据结构-如果.NET实现不同(或更改),则许多软件都将崩溃。
Bevan 2015年

@tehDorf我对此的看法(如果要进行讨论)是:“如果排序影响您的方法/算法的操作,则应明确地对列表进行排序,或者让您的API使用适当的接口/类,例如IOrderedEnumerable或SortedList,否则除非明确地声明,否则您不应该依赖于特定的实现来执行特定的方式。”您还必须注意对List <T>进行显式排序,因为其实现使用了不稳定的排序。
aolszowka 2015年

1
@achuthakrishnan他们是独立的问题。列表保证项目按添加到列表的顺序保留。当您使用LINQ Where()方法过滤列表时,您依赖于实现顺序考虑顺序中的项目。确实发生了(请参见referencesource.microsoft.com/System.Core/System/Linq/…),但是MSDN中未对此进行记录。我建议使用确实emp.LastOrDefault(x => x.empDept = 'abc')作为文档来LastOrDefault()表明它保持了项目的顺序。
Bevan

36

这是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);

这可能与您的问题有关。请注意,我的代码未经测试!

编辑:或者,如果适用于您的情况,则可以Sort使用自定义比较器(IComparer)。


没有足够仔细地阅读Bevan的回答,我只是介绍了他的观点。
乔尔·古德温

9
是的,但是您已经详细说明了Bevan的答案,并提供了一些解决方案代码的示例,因此您的答案不是互斥的,我已投票支持您。
詹森·S

9

正如Bevan所说,但请记住,列表索引是从0开始的。如果要将元素移到列表的最前面,则必须将其插入索引0(而不是示例中所示的1)。


1

这是我将代码在列表中的一个位置下移的代码:

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;
}

folderImagesListBox当然是a ,所以列表是a ListBox.ObjectCollection,而不是a List<T>,但它确实是从继承的,IList因此其行为应相同。这有帮助吗?

当然,前者仅在所选项目不是列表中的最后一项时才起作用,而后者在所选项目不是第一项时才起作用。


1

如果要更改操作顺序,将避免出现奇怪的行为:首先将值插入列表中的正确位置,然后从其第一个位置删除它。确保按他的索引删除它,因为如果要按引用将其删除,则可以将它们都删除...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.