【问题标题】:How can I find the last element in a List<>?如何找到 List<> 中的最后一个元素?
【发布时间】:2010-11-17 19:54:50
【问题描述】:

以下是我的代码的摘录:

public class AllIntegerIDs 
{
    public AllIntegerIDs() 
    {            
        m_MessageID = 0;
        m_MessageType = 0;
        m_ClassID = 0;
        m_CategoryID = 0;
        m_MessageText = null;
    }
    
    ~AllIntegerIDs()
    {
    }

    public void SetIntegerValues (int messageID, int messagetype,
        int classID, int categoryID)
    {
        this.m_MessageID = messageID;
        this.m_MessageType = messagetype;
        this.m_ClassID = classID;
        this.m_CategoryID = categoryID;
    }
    
    public string m_MessageText;
    public int m_MessageID;
    public int m_MessageType;
    public int m_ClassID;
    public int m_CategoryID;
}

我正在尝试在我的main() 功能代码中使用以下内容:

List<AllIntegerIDs> integerList = new List<AllIntegerIDs>();

/* some code here that is ised for following assignments*/
{
   integerList.Add(new AllIntegerIDs());
   index++;
   integerList[index].m_MessageID = (int)IntegerIDsSubstring[IntOffset];
   integerList[index].m_MessageType = (int)IntegerIDsSubstring[IntOffset + 1];
   integerList[index].m_ClassID = (int)IntegerIDsSubstring[IntOffset + 2];
   integerList[index].m_CategoryID = (int)IntegerIDsSubstring[IntOffset + 3];
   integerList[index].m_MessageText = MessageTextSubstring;
}

问题在这里:我正在尝试使用 for 循环打印列表中的所有元素:

for (int cnt3 = 0 ; cnt3 <= integerList.FindLastIndex ; cnt3++) //<----PROBLEM HERE
{
   Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\n", integerList[cnt3].m_MessageID,integerList[cnt3].m_MessageType,integerList[cnt3].m_ClassID,integerList[cnt3].m_CategoryID, integerList[cnt3].m_MessageText);
}

我想找到最后一个元素,以便在我的 for 循环中将 cnt3 等同起来,并打印出 List 中的所有条目。列表中的每个元素都是类AllIntegerIDs 的对象,如上面代码示例中所述。如何找到列表中最后一个有效条目?

我应该使用integerList.Find(integerList[].m_MessageText == null;之类的东西吗?

如果我使用它,它将需要一个范围从 0 到最大值的索引。意味着我将不得不使用另一个我不打算使用的 for 循环。有没有更短/更好的方法?

【问题讨论】:

  • @Viren:我缩进了代码以使其正确显示。如果你在我手下进行了编辑,你能确保我没有撤消它们吗?
  • 与您的问题无关,但除非需要,否则您真的不应该实现终结器。
  • 与问题无关,但为了可读性和可维护性,我建议您使用AllIntegerIDs newItem = new AllIntegerID();,使用它来分配所有字段并然后调用integerList.Add(newItem)。或者使用属性而不是字段并使用 C# 3.0 对象初始化器语法。

标签: c# list


【解决方案1】:

要获取集合的最后一项,请使用 LastOrDefault()Last() 扩展方法

var lastItem = integerList.LastOrDefault();

var lastItem = integerList.Last();

记得添加using System.Linq;,否则此方法将无法使用。

【讨论】:

  • 是的,这是最好的方法,Last 和 LastOrDefault 针对 Lists 进行了优化
  • @Gusdor 我没有看到它记录在案,但我倾向于直接求助于源代码(或使用 Resharper、dotPeek 或 ILSpy 之类的反汇编程序)。从那里我可以看到 First,FirstOrDefault,Last,LastOrDefault,Single,SingleOrDefault,ElementAtElementAtOrDefault 针对 IList&lt;TSource&gt; 和 @9876543335@ 进行了优化@ 针对 ICollection&lt;TSource&gt; 进行了优化,Cast&lt;TResult&gt; 针对 IEnumerable&lt;TResult&gt; 进行了优化。
  • 确保添加using System.Linq;
  • @chillitom System.Linq.Enumerable 公开的扩展方法并不是真正的“优化”。这是Enumerable.Last 方法的代码。
  • @chillitom 在阅读了System.Linq.Enumerable.Last 的源代码后,我同意 0b101010 - Last() 代码 不是“为 List&lt;&gt;s 优化” - @987654346 @ 只是一个丑陋的包装器,如果参数是IList,则默认为return list[list.Count-1],并且在列表中迭代直到最后,以防它不是......使它非常糟糕如果IListLinkedList 的解决方案,因为索引器将不必要地遍历整个列表(我没有找到在c# 源中使用索引> Count/2 在Item[] 上向后迭代的覆盖,YMMV)
【解决方案2】:

如果您只想访问列表中的最后一项,您可以这样做

if(integerList.Count>0)
{
   //  pre C#8.0 : var item = integerList[integerList.Count - 1];
   //  C#8.0 : 
   var item = integerList[^1];
}

要获取列表中的项目总数,您可以使用 Count 属性

var itemCount = integerList.Count;

【讨论】:

  • @Jared 我想你忘了在第一行之前添加这一行 "if(integerList.Count != 0)"
  • 恕我直言,这不应该成为最佳答案,它读起来很糟糕,如果计数为零,就有可能出错。 CleanCode™ 方法是使用Last/LastOrDefault,如下所述。
  • 如前所述,此答案未考虑列表为空且不应使用恕我直言的情况。
  • @chillitom @merrr 使用 LINQ 扩展方法没有帮助。如果列表为空,Enumerable.Last 将抛出异常。如果您调用Enumerable.LastOrDefault 并传递值类型列表,则如果列表为空,则将返回默认值。因此,如果您从 List&lt;int&gt; 返回 0,您将不知道列表是空的还是最后一个值为 0。简而言之,无论您决定使用哪种检索机制,您都需要检查 Count
  • @chillitom 每个人都有自己的。在您知道已填充列表的情况下,我认为var element = list[list.Count - 1] 非常简洁易读。无需调用扩展方法
【解决方案3】:

在 C# 8.0 中,您可以使用 ^ 运算符 full explanation 获取最后一项

List<char> list = ...;
var value = list[^1]; 

// Gets translated to 
var value = list[list.Count - 1];

【讨论】:

    【解决方案4】:

    让我们找到问题的根源,如何安全地处理 List 的最后一个元素...

    假设

    List<string> myList = new List<string>();
    

    然后

    //NOT safe on an empty list!
    string myString = myList[myList.Count -1];
    
    //equivalent to the above line when Count is 0, bad index
    string otherString = myList[-1];
    

    “count-1”是个坏习惯,除非你首先保证列表不为空。

    除了检查空列表之外,没有其他方便的方法。

    我能想到的最短的方法是

    string myString = (myList.Count != 0) ? myList [ myList.Count-1 ] : "";
    

    您可以全力以赴,创建一个始终返回 true 的委托,并将其传递给 FindLast,FindLast 将返回最后一个值(如果列表为空,则返回默认构造的 valye)。此函数从列表末尾开始,因此将是大 O(1) 或常数时间,尽管方法通常是 O(n)。

    //somewhere in your codebase, a strange delegate is defined
    private static bool alwaysTrue(string in)
    {
        return true;
    }
    
    //Wherever you are working with the list
    string myString = myList.FindLast(alwaysTrue);
    

    如果算上委托部分,FindLast 方法很丑,但它只需要声明一个地方。如果列表为空,它将为字符串返回列表类型“”的默认构造值。将 alwaysTrue 委托更进一步,使其成为模板而不是字符串类型,会更有用。

    【讨论】:

    • 委托可以替换为 lambda 表达式:myList.FindLast(_unused_variable_name =&gt; true); 无论类型如何,这都可以工作。较短的版本是 myList.FindLast(_ =&gt; true);,但我发现只是下划线(或任何其他单个字符标识符)有时会有点混乱。
    【解决方案5】:
    int lastInt = integerList[integerList.Count-1];
    

    【讨论】:

      【解决方案6】:

      改变

      for (int cnt3 = 0 ; cnt3 <= integerList.FindLastIndex ; cnt3++)
      

      for (int cnt3 = 0 ; cnt3 < integerList.Count; cnt3++)
      

      【讨论】:

      • foreach 通常使用起来更方便,但速度稍慢。
      • 如果使用 Count... 执行 -1 否则会出现索引错误。 for (int cnt3 = 0 ; cnt3
      • 这就是我将
      • @Eric:它曾经比较慢,但在 JIT 中遇到这种情况是微不足道的,所以如果他们现在还没有,我会感到惊讶。 :不知道:
      • @IPX Ares:似乎仍然是一个问题,具体取决于您正在迭代的数据类型:stackoverflow.com/questions/365615/…
      【解决方案7】:

      虽然这是 11 年前发布的,但我确信正确的答案数量比现有的多一个!

      你也可以这样做;

      
      if (integerList.Count > 0) 
         var item = integerList[^1];
      
      

      请参阅几个月前 MS C# 文档 here 上的教程帖子。

      我个人仍然会坚持使用LastOrDefault() / Last(),但我想我会分享这个。

      编辑; 刚刚意识到另一个答案在另一个文档链接中提到了这一点。

      【讨论】:

        【解决方案8】:

        使用Count 属性。最后一个索引是Count - 1

        for (int cnt3 = 0 ; cnt3 < integerList.Count; cnt3++)
        

        【讨论】:

          【解决方案9】:

          为什么不直接使用 List 的 Count 属性?

          for(int cnt3 = 0; cnt3 < integerList.Count; cnt3++)
          

          【讨论】:

            【解决方案10】:

            独立于您的原始问题,如果您捕获对局部变量的引用而不是多次索引到您的列表中,您将获得更好的性能:

            AllIntegerIDs ids = new AllIntegerIDs();
            ids.m_MessageID = (int)IntegerIDsSubstring[IntOffset];
            ids.m_MessageType = (int)IntegerIDsSubstring[IntOffset + 1];
            ids.m_ClassID = (int)IntegerIDsSubstring[IntOffset + 2];
            ids.m_CategoryID = (int)IntegerIDsSubstring[IntOffset + 3];
            ids.m_MessageText = MessageTextSubstring;
            integerList.Add(ids);
            

            在您的for 循环中:

            for (int cnt3 = 0 ; cnt3 < integerList.Count ; cnt3++) //<----PROBLEM HERE
            {
               AllIntegerIDs ids = integerList[cnt3];
               Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\n",
                  ids.m_MessageID,ids.m_MessageType,ids.m_ClassID,ids.m_CategoryID, ids.m_MessageText);
            }
            

            【讨论】:

              【解决方案11】:

              我不得不同意 foreach 会更容易,比如

              foreach(AllIntegerIDs allIntegerIDs in integerList)
              {
              Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\n", allIntegerIDs.m_MessageID,
              allIntegerIDs.m_MessageType,
              allIntegerIDs.m_ClassID,
              allIntegerIDs.m_CategoryID,
              allIntegerIDs.m_MessageText);
              }
              

              另外我建议您添加属性来访问您的信息而不是公共字段,根据您的 .net 版本,您可以像 public int MessageType {get; set;} 一样添加它,并从您的公共字段、属性等中删除 m_不应该在那里。

              【讨论】:

                猜你喜欢
                • 2013-04-11
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-04-28
                • 2014-09-10
                • 2013-06-26
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多