Wie finde ich das letzte Element in einer Liste <>?


172

Das Folgende ist ein Auszug aus meinem 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;
}

Ich versuche, in meinem main () - Funktionscode Folgendes zu verwenden:

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

Das Problem ist hier: Ich versuche, alle Elemente in meiner Liste mit einer for-Schleife zu drucken:

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

Ich möchte das letzte Element finden, damit ich cnt3 in meiner for-Schleife gleichsetze und alle Einträge in der Liste ausdrucke. Jedes Element in der Liste ist ein Objekt der Klasse AllIntegerIDs, wie oben im Codebeispiel erwähnt. Wie finde ich den letzten gültigen Eintrag in der Liste?

Sollte ich so etwas wie integerList.Find (integerList []. M_MessageText == null;

Wenn ich das benutze, braucht es einen Index, der von 0 bis zu jedem Maximum reicht. Das heißt, ich muss eine andere for-Schleife verwenden, die ich nicht verwenden möchte. Gibt es einen kürzeren / besseren Weg?

Danke, Viren


@Viren: Ich habe den Code eingerückt, damit er richtig angezeigt wird. Wenn Sie unter mir Änderungen vorgenommen haben, können Sie sicherstellen, dass ich sie nicht rückgängig gemacht habe?
Sam Harwell

8
Nicht im Zusammenhang mit Ihrer Frage, aber Sie sollten einen Finalizer nur dann implementieren, wenn er benötigt wird.
Brian Rasmussen

Nicht im Zusammenhang mit der Frage, aber aus Gründen der Lesbarkeit und Wartbarkeit empfehle ich Ihnen AllIntegerIDs newItem = new AllIntegerID();, alle Felder zuzuweisen und dann aufzurufen integerList.Add(newItem). Oder verwenden Sie Eigenschaften anstelle von Feldern und verwenden Sie die C # 3.0-Objektinitialisierersyntax.
Thorarin

Antworten:


206

Wenn Sie nur auf das letzte Element in der Liste zugreifen möchten, können Sie dies tun

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

Um die Gesamtzahl der Elemente in der Liste abzurufen, können Sie die Count-Eigenschaft verwenden

var itemCount = integerList.Count;

17
@ Jared Ich denke, Sie haben vergessen, diese Zeile "if (integerList.Count! = 0)" vor der ersten Zeile
hinzuzufügen

21
Meiner Meinung nach verdient dies nicht die beste Antwort, es liest sich schrecklich und lässt die Möglichkeit eines Fehlers, wenn die Anzahl Null ist. Der CleanCode ™ -Ansatz besteht darin, Last/ LastOrDefaultwie unten erwähnt zu verwenden.
Chillitom

2
Wie bereits erwähnt, berücksichtigt diese Antwort nicht die Situation, in der die Liste leer ist, und sollte meiner Meinung nach nicht verwendet werden.
Merrr

2
@chillitom @merrr Die Verwendung von LINQ-Erweiterungsmethoden hilft nicht. Enumerable.Lastlöst eine Ausnahme aus, wenn die Liste leer ist. Wenn Sie Enumerable.LastOrDefaulteine Liste von Werttypen aufrufen und übergeben, wird der Standardwert zurückgegeben, wenn die Liste leer ist. Wenn Sie also 0 von a zurückerhalten, List<int>wissen Sie nicht, ob die Liste leer oder der letzte Wert 0 war. Kurz gesagt, Sie müssen den CountAbrufmechanismus überprüfen, den Sie verwenden möchten.
0b101010

4
@chillitom Jeder für sich. In Fällen, in denen Sie wissen, dass eine Liste ausgefüllt var element = list[list.Count - 1]ist, halte ich sie für sehr prägnant und lesbar. Keine Notwendigkeit, Erweiterungsmethoden
aufzurufen

276

Verwenden Sie die Erweiterungsmethoden LastOrDefault () und Last (), um das letzte Element einer Sammlung abzurufen

var lastItem = integerList.LastOrDefault();

ODER

var lastItem = integerList.Last();

Denken Sie daran, hinzuzufügen using System.Linq;, oder diese Methode ist nicht verfügbar.


17
Ja, dies ist der beste Weg, Last und LastOrDefault sind für List <> s
chillitom

2
@Gusdor Ich habe es nicht dokumentiert gesehen, aber ich neige dazu, mich für diese Dinge direkt an die Quellen zu wenden (oder einen Disassembler wie Resharper, dotPeek oder ILSpy zu verwenden). Von dort aus kann ich sehen First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAtund ElementAtOrDefaultsind optimiert für IList<TSource>, Countund Containssind für eine optimierte ICollection<TSource>und Cast<TResult>ist optimiert für IEnumerable<TResult>.
Chillitom

8
Stellen Sie sicher, dass Sie hinzufügenusing System.Linq;
Hybrid

4
@chillitom Die von bereitgestellten Erweiterungsmethoden System.Linq.Enumerablesind nicht wirklich "optimiert". Hier ist der Code für die Enumerable.LastMethode.
0b101010

4
@chillitom Nach dem Lesen der Quelle von System.Linq.Enumerable.Laststimme ich 0b101010 zu - der Last()Code ist nicht "für List<>s optimiert " - Last()ist nur ein hässlicher Wrapper, der standardmäßig verwendet wird, return list[list.Count-1]falls das Argument ein ist IList, und die Liste bis zum Ende durchläuft, falls Es ist nicht ... eine sehr schlechte Lösung, wenn IListes sich um eine handelt LinkedList, da der Indexer nur unnötig die gesamte Liste durchläuft (ich habe keine Überschreibung gefunden, die Item[]mit index> Count / 2 in den c # -Quellen, YMMV, rückwärts iteriert )

20

Gehen wir der Frage auf den Grund, wie das letzte Element einer Liste sicher angesprochen werden kann ...

Vorausgesetzt

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

Dann

//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" ist eine schlechte Angewohnheit, es sei denn, Sie garantieren zuerst, dass die Liste nicht leer ist.

Es gibt keine bequeme Möglichkeit, nach der leeren Liste zu suchen, außer dies zu tun.

Der kürzeste Weg, den ich mir vorstellen kann, ist

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

Sie können alles daran setzen, einen Delegaten zu erstellen, der immer true zurückgibt, und ihn an FindLast übergeben, der den letzten Wert zurückgibt (oder standardmäßig einen Wert, wenn die Liste leer ist). Diese Funktion beginnt am Ende der Liste, ist also Big O (1) oder konstante Zeit, obwohl die Methode normalerweise O (n) ist.

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

Die FindLast-Methode ist hässlich, wenn Sie den Delegatenteil zählen, sie muss jedoch nur an einer Stelle deklariert werden. Wenn die Liste leer ist, wird ein standardmäßig erstellter Wert vom Listentyp "" für Zeichenfolge zurückgegeben. Es wäre nützlicher, den delegierten alwaysTrue-Schritt weiter zu gehen und ihn anstelle des Zeichenfolgentyps zu einer Vorlage zu machen.


2
Der Delegat kann durch einen Lambda-Ausdruck ersetzt werden: myList.FindLast(_unused_variable_name => true);Dies funktioniert unabhängig vom Typ. Eine kürzere Version ist myList.FindLast(_ => true);, aber ich finde, dass nur der Unterstrich (oder eine andere Einzelzeichen-ID) manchmal etwas verwirrend sein kann.
Bob


5

Veränderung

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

zu

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

foreach ist oft bequemer zu bedienen, aber etwas langsamer.
Eric J.

Wenn Sie Count ... verwenden, geben Sie -1 ein, da sonst ein Indexfehler angezeigt wird. für (int cnt3 = 0; cnt3 <integerList.Count - 1; cnt3 ++)
RiddlerDev

4
Deshalb habe ich <= in <geändert. Der Code ist korrekt wie gepostet :-)
Eric J.

@Eric: Früher war es langsamer, aber es ist ein trivialer Fall, in der JIT zu landen, also wäre ich überrascht, wenn sie es bis jetzt nicht getan hätten. : Keine Ahnung:
Sam Harwell

1
@IPX Ares: Scheint immer noch ein Problem zu sein, abhängig von dem Datentyp, den Sie iterieren: stackoverflow.com/questions/365615/…
Eric J.

2

Verwenden Sie die CountEigenschaft. Der letzte Index wird sein Count - 1.

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

2

Sie finden es, indem Sie zuerst die Anzahl der Elemente in der Liste zählen, z

int count = list.Count();

Dann können Sie die Anzahl 1 indizieren, um das letzte Element in der Liste zu erhalten, z

int lastNumber = list[count - 1];

2
Bitte posten Sie keine doppelten Antworten.
Ian Mercer


1

Warum nicht einfach die Count-Eigenschaft in der Liste verwenden?

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

0

Unabhängig von Ihrer ursprünglichen Frage erzielen Sie eine bessere Leistung, wenn Sie Verweise auf lokale Variablen erfassen, anstatt sie mehrmals in Ihre Liste zu indizieren:

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

Und in Ihrer forSchleife:

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

Ich müsste zustimmen, dass ein Foreach viel einfacher wäre

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

Außerdem würde ich vorschlagen, dass Sie Eigenschaften hinzufügen, um auf Ihre Informationen zuzugreifen, anstatt auf öffentliche Felder. Abhängig von Ihrer .net-Version können Sie diese hinzufügen public int MessageType {get; set;}und die m_öffentlichen Felder, Eigenschaften usw. entfernen, da sie nicht vorhanden sein sollten.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.