243

The following is an extract from my code:

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

I am trying to use the following in my main() function code:

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

Problem is here: I am trying to print all elements in my List using a for loop:

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

I want to find the last element so that I equate cnt3 in my for loop and print out all entries in the List. Each element in the list is an object of the class AllIntegerIDs as mentioned above in the code sample. How do I find the last valid entry in the List?

Should I use something like integerList.Find(integerList[].m_MessageText == null;?

If I use that it will need an index that will range from 0 to whatever maximum. Means I will have to use another for loop which I do not intend to use. Is there a shorter/better way?

Neuron
  • 5,141
  • 5
  • 38
  • 59
zack
  • 7,115
  • 14
  • 53
  • 63
  • @Viren: I indented the code to make it show properly. If you made edits under me can you make sure I didn't undo them? – Sam Harwell Aug 07 '09 at 20:51
  • 9
    Not related to your question, but you really should not implement a finalizer unless it is needed. – Brian Rasmussen Aug 07 '09 at 20:52
  • Not related to the question, but for readability and maintainability, I suggest you do `AllIntegerIDs newItem = new AllIntegerID();`, use that to assign all fields and *then* call `integerList.Add(newItem)`. Or use properties rather than fields and use C# 3.0 object initializer syntax. – Thorarin Aug 07 '09 at 20:59

12 Answers12

377

To get the last item of a collection use LastOrDefault() and Last() extension methods

var lastItem = integerList.LastOrDefault();

OR

var lastItem = integerList.Last();

Remeber to add using System.Linq;, or this method won't be available.

Almo
  • 15,538
  • 13
  • 67
  • 95
Kundan Singh Chouhan
  • 13,952
  • 4
  • 27
  • 32
  • 27
    Yep this is the best way, Last and LastOrDefault are optimised for List<>s – chillitom May 24 '13 at 15:08
  • @chillitom I use `Last()` a lot but recently started to wonder if it treated `IList` instances properly. Where can I read about this? – Gusdor Dec 04 '13 at 12:18
  • 4
    @Gusdor I've not seen it documented but I tend to just turn to the sources (or use a disassembler like Resharper,dotPeek or ILSpy) directly for these things. From there I can see that `First`,`FirstOrDefault`,`Last`,`LastOrDefault`,`Single`,`SingleOrDefault`,`ElementAt` and `ElementAtOrDefault` are optimised for `IList`, `Count` and `Contains` are optimised for `ICollection` and `Cast` is optimised for `IEnumerable`. – chillitom Dec 04 '13 at 17:52
  • 2
    http://www.dotnetframework.org/default.aspx/4@0/4@0/untmp/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/fx/src/Core/System/Linq/Enumerable@cs/1407647/Enumerable@cs – chillitom Dec 04 '13 at 17:57
  • 13
    make sure to add `using System.Linq;` – Hybrid Feb 28 '14 at 18:13
  • 4
    @chillitom The extension methods exposed by `System.Linq.Enumerable` aren't really 'optimised'. Here's the code for the `Enumerable.Last` method. – 0b101010 Nov 07 '14 at 16:07
  • @chillitom Couldn't post the code as it was too long but suffice to say there is a speculative cast `as IList` in there which certainly isn't performance optimised. – 0b101010 Nov 07 '14 at 16:18
  • Doesn't work for assignment though *(but the accepted one does)*. – Hi-Angel Jul 15 '15 at 08:20
  • 4
    @chillitom after reading the source of `System.Linq.Enumerable.Last`, I agree with 0b101010 - the `Last()` code **ain't** "optimized for `List<>`s" - `Last()` is just an ugly wrapper, which defaults to `return list[list.Count-1]` in case the argument is an `IList`, and **iterates** over the list till the end in case it's not... making it a very poor solution if the `IList` is a `LinkedList`, since the indexer will just go through the entire list needlessly (I haven't found an override iterating backwards on `Item[]` with index > Count/2 in the c# sources, YMMV) –  Jan 02 '16 at 16:47
  • 2
    @vaxquis - to clarify, it *is* optimized for `List<>`. Specifically, all descendents of `IList`. Though, as you say, it is regrettably not optimized for `LinkedList<>`. – ToolmakerSteve Dec 04 '19 at 19:48
264

If you just want to access the last item in the list you can do

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

to get the total number of items in the list you can use the Count property

var itemCount = integerList.Count;
martijnn2008
  • 3,552
  • 5
  • 30
  • 40
Jared
  • 8,390
  • 5
  • 38
  • 43
  • true , but is there maybe an inner way this could be done . for instance iv'e come across a situation where i need to use a long cast on a collection in order to expose an inner collection , this makes my code look pretty ugly Checker checker = (pipes[from].ItemsSource as ObservableCollection).ElementAt((pipes[from].ItemsSource as ObservableCollection).Count - 1); any ideas ? – eran otzap Mar 18 '12 at 17:18
  • 20
    @Jared I think you forgot to add this line "if(integerList.Count != 0)" before the first line – prabhakaran May 31 '12 at 10:04
  • 27
    IMHO this doesn't deserve to be the top answer, it reads terribly and leaves the chance for an error if count is zero. The _CleanCode™_ approach would be to use `Last`/`LastOrDefault` as mentioned below. – chillitom Dec 04 '13 at 18:04
  • 2
    As pointed out previously, this answer does not take into account situation when the list is empty and should not be used IMHO. – merrr Apr 18 '14 at 21:47
  • 2
    @chillitom @merrr Using LINQ extension methods does not help. `Enumerable.Last` will throw an exception if the list is empty. If you call `Enumerable.LastOrDefault` and pass a list of value types the default value will be returned if the list is empty. So if you get 0 back from a `List` you won't know if the list was empty or the last value was 0. In short, you need to check the `Count` whichever retrieval mechanism you decide to use. – 0b101010 Nov 07 '14 at 16:23
  • 1
    @0b101010 true, in the case of T being a primitive then you'd also need to check that, def. prefer LastOrDefault though when dealing with non-null reference types – chillitom Nov 07 '14 at 16:30
  • 4
    @chillitom Each to their own. In instances where you know a list is populated I think `var element = list[list.Count - 1]` is very succinct and readable. No need to be invoking extension methods – 0b101010 Nov 07 '14 at 17:15
  • I need to conditionally access the last element. This approach is closer to the metal and is generic enough to use in multiple situations. IMHO, `LastOrDefault` is most helpful when it provides intent. – Trevor.Screws May 21 '21 at 12:15
34

In C# 8.0 you can get the last item with ^ operator full explanation

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

// Gets translated to 
var value = list[list.Count - 1];
Alex Peng
  • 411
  • 5
  • 4
  • Caution: If `list` is empty, then `list[^1]` throws an exception. See other answers for more in depth solutions. Potential alternatives: .NET 5 and below: `list.DefaultIfEmpty(char('z')).Last()` or `list.LastOrDefault()` ... .NET 6 and above: `list.LastOrDefault(char('z'))` – Lee Grissom Nov 09 '22 at 00:33
22

Lets get at the root of the question, how to address the last element of a List safely...

Assuming

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

Then

//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" is a bad habit unless you first guarantee the list is not empty.

There is not a convenient way around checking for the empty list except to do it.

The shortest way I can think of is

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

you could go all out and make a delegate that always returns true, and pass it to FindLast, which will return the last value (or default constructed valye if the list is empty). This function starts at the end of the list so will be Big O(1) or constant time, despite the method normally being 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);

The FindLast method is ugly if you count the delegate part, but it only needs to be declared one place. If the list is empty, it will return a default constructed value of the list type "" for string. Taking the alwaysTrue delegate a step further, making it a template instead of string type, would be more useful.

JFDev
  • 221
  • 2
  • 2
  • 2
    The delegate can be replaced with a lambda expression: `myList.FindLast(_unused_variable_name => true);` This will work regardless of type. A shorter version is `myList.FindLast(_ => true);`, but I find just the underscore (or any other single character identifier) can be a bit confusing at times. – Bob Jan 29 '13 at 05:56
8
int lastInt = integerList[integerList.Count-1];
Dan Diplo
  • 25,076
  • 4
  • 67
  • 89
8

Though this was posted 11 years ago, I'm sure the right number of answers is one more than there are!

You can also doing something like;


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

See the tutorial post on the MS C# docs here from a few months back.

I would personally still stick with LastOrDefault() / Last() but thought I'd share this.

EDIT; Just realised another answer has mentioned this with another doc link.

Pratik Thanki
  • 246
  • 2
  • 4
6

Change

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

to

for (int cnt3 = 0 ; cnt3 < integerList.Count; cnt3++)
Eric J.
  • 147,927
  • 63
  • 340
  • 553
  • foreach is often more convenient to use, but is SLIGHTLY slower. – Eric J. Aug 07 '09 at 20:52
  • if using Count... do a -1 or you will get an index error. for (int cnt3 = 0 ; cnt3 < integerList.Count - 1; cnt3++) – RiddlerDev Aug 07 '09 at 21:03
  • 4
    That's why I changed <= to <. The code is correct as posted :-) – Eric J. Aug 07 '09 at 21:05
  • @Eric: It used to be slower, but it's a trivial case to hit in the JIT so I'd be surprised if they haven't by now. :dunno: – Sam Harwell Aug 07 '09 at 21:23
  • 1
    @IPX Ares: Seems to still be an issue, depending on the data type you are iterating: http://stackoverflow.com/questions/365615/in-c-which-loop-runs-faster-for-or-foreach#365622 – Eric J. Aug 07 '09 at 21:34
3

Use the Count property. The last index will be Count - 1.

for (int cnt3 = 0 ; cnt3 < integerList.Count; cnt3++)
Spencer Ruport
  • 34,865
  • 12
  • 85
  • 147
1

Why not just use the Count property on the List?

for(int cnt3 = 0; cnt3 < integerList.Count; cnt3++)
Brandon
  • 68,708
  • 30
  • 194
  • 223
0

Independent of your original question, you will get better performance if you capture references to local variables rather than index into your list multiple times:

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

And in your for loop:

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);
}
dahlbyk
  • 75,175
  • 8
  • 100
  • 122
0

I prefer to use something like this. Simple and easy to read.

var lastItem = list[list.Count - 1];
Excorpse
  • 29
  • 1
  • 3
-1

I would have to agree a foreach would be a lot easier something like

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

Also I would suggest you add properties to access your information instead of public fields, depending on your .net version you can add it like public int MessageType {get; set;} and get rid of the m_ from your public fields, properties etc as it shouldnt be there.

Michael Ciba
  • 561
  • 3
  • 6