如何在List <>中找到最后一个元素?


172

以下是我的代码的摘录:

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

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

我应该使用诸如integerList.Find(integerList []。m_MessageText == null;

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

谢谢,维伦


@Viren:我缩进代码以使其正确显示。如果您在我的支持下进行编辑,可以确保我没有撤消它们吗?
山姆·哈威尔

8
与您的问题无关,但除非有必要,否则您实际上不应该实现终结器。
布赖恩·拉斯穆森

与该问题无关,但出于可读性和可维护性的考虑,建议您这样做AllIntegerIDs newItem = new AllIntegerID();,使用它来分配所有字段,然后调用integerList.Add(newItem)。或者使用属性而不是字段,并使用C#3.0对象初始化程序语法。
Thorarin

Answers:


208

如果您只想访问列表中的最后一项,则可以执行

if(integerList.Count>0)
{
   var item = integerList[integerList.Count - 1];
}

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

var itemCount = integerList.Count;

17
@Jared我认为您忘记在第一行之前添加此行“ if(integerList.Count!= 0)”
prabhakaran,2012年

21
恕我直言,这不应该是最好的答案,它的读取非常糟糕,如果count为零,则可能会出错。该CleanCode™方法是使用Last/ LastOrDefault如下所述。
chillitom 2013年

2
如前所述,当列表为空且不应用于恕我直言时,此答案未考虑情况。
merrr 2014年

2
@chillitom @merrr使用LINQ扩展方法无济于事。Enumerable.Last如果列表为空,将抛出异常。如果调用Enumerable.LastOrDefault并传递值类型列表,则该列表为空时将返回默认值。因此,如果从a返回0,List<int>您将不知道列表是否为空或最后一个值为0。简而言之,您需要检查Count决定使用哪种检索机制。
0b101010 2014年

4
@chillitom每个都有。在您知道列表已填充的情况下,我认为var element = list[list.Count - 1]它非常简洁易读。无需调用扩展方法
0b101010 2014年

276

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

var lastItem = integerList.LastOrDefault();

要么

var lastItem = integerList.Last();

请记住添加using System.Linq;,否则此方法将不可用。


17
是的,这是最好的方法,Last和LastOrDefault已针对List <> s进行了优化
chillitom

2
@Gusdor我没有看到它的记录,但我倾向于直接针对这些东西使用源文件(或使用诸如Resharper,dotPeek或ILSpy之类的反汇编程序)。从那里,我可以看到FirstFirstOrDefaultLastLastOrDefaultSingleSingleOrDefaultElementAtElementAtOrDefault为优化IList<TSource>CountContains进行优化ICollection<TSource>,并Cast<TResult>进行了优化IEnumerable<TResult>
chillitom

8
确保添加using System.Linq;
混合

4
@chillitom公开的扩展方法System.Linq.Enumerable并没有真正“优化”。这是该Enumerable.Last方法的代码。
0b101010 2014年

4
@chillitom阅读了的源代码后System.Linq.Enumerable.Last,我同意0b101010- Last()代码 “针对List<>s 优化”- Last()只是一个丑陋的包装器,默认return list[list.Count-1]情况下以防参数为an IList,并在列表上进行迭代直至万一这不是...如果ILista为a LinkedList,这将是一个非常糟糕的解决方案,因为索引器将不必要地遍历整个列表(我没有发现Item[]在C#源代码YMMV中使用index> Count / 2 向后迭代的重写)

20

让我们从问题的根源开始,即如何安全地处理列表的最后一个元素...

假设

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,后者将返回最后一个值(如果列表为空,则返回默认构造的valye)。尽管此方法通常为O(n),但此函数从列表的末尾开始,因此将为Big O(1)或恒定时间。

//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委托设为模板而不是字符串类型,将更加有用。


2
可以用lambda表达式代替该委托:myList.FindLast(_unused_variable_name => true);无论类型如何,该表达式都可以工作。较短的版本是myList.FindLast(_ => true);,但我发现下划线(或任何其他单个字符标识符)有时可能会有些混乱。
鲍勃,


5

更改

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

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

foreach通常更方便使用,但速度稍慢。
Eric J.

如果使用Count ...,则为-1,否则会出现索引错误。for(int cnt3 = 0; cnt3 <integerList.Count-1; cnt3 ++)
RiddlerDev

4
这就是为什么我将<=更改为<。代码正确无误(
Eric J.

@Eric:过去速度较慢,但​​要打入准时制只是一个微不足道的案例,所以如果他们现在还没有,我会感到惊讶。:dunno:
萨姆·哈威尔2009年

1
@IPX战神:似乎依然是一个问题,这取决于数据类型你迭代: stackoverflow.com/questions/365615/...
埃里克J.

2

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

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


2

在C#8.0中,您可以获得带有^运算符的完整说明的最后一项

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

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

1

为什么不只使用列表中的Count属性?

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

0

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

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

-1

我将不得不同意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_从您的公共字段,属性等,因为它不应该在那里。


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.