【问题标题】: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&lt;T&gt;保证订单吗?

相关:

Does a List<T> guarantee that items will be returned in the order they were added?

【问题讨论】:

标签: c# .net collections


【解决方案1】:

List&lt;&gt; 类确实保证了排序 - 事物将按照您添加它们的顺序保留在列表中,包括重复项,除非您明确对列表进行排序。

根据 MSDN:

...List "表示一个强类型的对象列表,可以 按索引访问。”

索引值必须保持可靠才能准确。因此,订单是有保证的。

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

你能把你的代码简化成小到可以发布的东西吗?

【讨论】:

  • 对于任何未来的 googler,这里是来自 MSDN(粗体我的)List(T).Add 的确切引用 要添加到列表 end 的对象。对于引用类型,该值可以为 null。
  • 我们是否可以从 Microsoft 或 C# 规范中获得更明确的引用/参考? @aolszowka 的引用肯定似乎表明它确实保留了插入顺序,但从技术上讲,列表可以在添加项目后的任何时候重新排序集合,并且该语句仍然有效。我不想对此挑剔,但如果经理或 QA 真的让我捍卫这个职位,我不会对这句话感到非常自信。
  • 我可以想到两种有用的方法来获得一些确认。首先,read the source 并满足自己。其次,在任何好的计算机科学教科书中查看抽象数据类型List 的定义。就像QueueStack 一样,List 是一个定义明确、可预测且易于理解的数据结构 - 如果 .NET 实现不同(或者如果它发生变化),很多软件会休息。
  • @tehDorf 我对此的看法(如果要对此进行讨论)是:“如果排序影响您的方法/算法的操作,您应该明确排序列表或让您的 API 采用适当的接口/类,例如 IOrderedEnumerable 或 SortedList,否则您不应依赖特定的实现以某种方式运行,除非明确明确说明“您还必须小心明确排序 List,因为它们的实现使用了不稳定的排序。
  • @achuthakrishnan 它们是不同的问题。 List 保证项目按照添加到列表中的顺序保留。当您使用 LINQ Where() 方法过滤列表时,您依赖于实现来按顺序考虑序列中的项目。碰巧确实如此(请参阅referencesource.microsoft.com/System.Core/System/Linq/…),但这在 MSDN 中没有记录。我建议使用emp.LastOrDefault(x =&gt; 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&lt;T&gt;,但它确实继承自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 &gt; indexToMove,例如

      bool moveUp = (listInstance.IndexOf(itemToMoveTo) > indexToMove);
      listInstance.Remove(itemToMove);
      listInstance.Insert(indexToMoveTo, moveUp ? (itemToMoveTo - 1) : itemToMoveTo);
      

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

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

      【讨论】:

      • 没有仔细阅读 Bevan 的回复,我刚刚覆盖了他的基础。
      • 是的,但是您已经详细说明并给出了 Bevan 答案的示例以及一些解决方案代码,因此您的答案不是同义词,我已投票支持您。
      【解决方案5】:

      如果你改变操作的顺序,你会避免奇怪的行为: 首先将值插入列表中的正确位置,然后将其从他的第一个位置删除。确保你按他的索引删除它,因为如果你按引用删除它,你可能会同时删除它们......

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-08
        • 1970-01-01
        • 2011-04-19
        • 2010-10-01
        • 2010-10-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多