¿Cómo puedo encontrar el último elemento en una Lista <>?

173

Lo siguiente es un extracto de mi código:

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

Estoy tratando de usar lo siguiente en mi código de función 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;
}

El problema está aquí: estoy tratando de imprimir todos los elementos en mi lista usando un bucle 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);
}

Quiero encontrar el último elemento para poder cnt3 cnt3 en mi bucle for e imprimir todas las entradas en la lista. Cada elemento de la lista es un objeto de la clase AllIntegerID como se mencionó anteriormente en el ejemplo de código. ¿Cómo encuentro la última entrada válida en la Lista?

¿Debo usar algo como integerList.Find (integerList []. M_MessageText == null;

Si lo uso, necesitará un índice que oscilará entre 0 y el máximo. Significa que tendré que usar otro bucle for que no tengo la intención de usar. ¿Hay una forma más corta / mejor?

Gracias viren

zack
fuente
@Viren: sangré el código para que se muestre correctamente. Si hiciste ediciones debajo de mí, ¿puedes asegurarte de que no las deshaga?
Sam Harwell el
8
No está relacionado con su pregunta, pero realmente no debe implementar un finalizador a menos que sea necesario.
Brian Rasmussen
No relacionado con la pregunta, pero para facilitar la lectura y el mantenimiento, sugiero que lo haga AllIntegerIDs newItem = new AllIntegerID();, use eso para asignar todos los campos y luego llame integerList.Add(newItem). O use propiedades en lugar de campos y use la sintaxis de inicializador de objeto C # 3.0.
Thorarin el

Respuestas:

208

Si solo desea acceder al último elemento de la lista, puede hacerlo

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

para obtener el número total de elementos en la lista, puede usar la propiedad Count

var itemCount = integerList.Count;
Jared
fuente
17
@ Jared Creo que olvidó agregar esta línea "if (integerList.Count! = 0)" antes de la primera línea
prabhakaran
21
En mi humilde opinión, esto no merece ser la mejor respuesta, se lee terriblemente y deja la posibilidad de un error si el recuento es cero. El enfoque CleanCode ™ sería utilizar Last/ LastOrDefaultcomo se menciona a continuación.
chillitom
2
Como se señaló anteriormente, esta respuesta no tiene en cuenta la situación cuando la lista está vacía y no debe usarse en mi humilde opinión.
merrr
2
@chillitom @merrr El uso de métodos de extensión LINQ no ayuda. Enumerable.Lastlanzará una excepción si la lista está vacía. Si llama Enumerable.LastOrDefaulty pasa una lista de tipos de valores, se devolverá el valor predeterminado si la lista está vacía. Por lo tanto, si obtiene 0 de a List<int>, no sabrá si la lista estaba vacía o si el último valor fue 0. En resumen, debe verificar el Countmecanismo de recuperación que decida utilizar.
0b101010
44
@chillitom Cada uno a lo suyo. En los casos en que sabe que una lista está poblada, creo que var element = list[list.Count - 1]es muy sucinta y legible. No es necesario invocar métodos de extensión
0b101010
277

Para obtener el último elemento de un uso de la colección LastOrDefault () y Last () los métodos de extensión

var lastItem = integerList.LastOrDefault();

O

var lastItem = integerList.Last();

Recuerde agregar using System.Linq;, o este método no estará disponible.

Kundan Singh Chouhan
fuente
18
Sí, esta es la mejor manera, Last y LastOrDefault están optimizados para List <> s
chillitom
2
@Gusdor No lo he visto documentado, pero tiendo a recurrir directamente a las fuentes (o usar un desensamblador como Resharper, dotPeek o ILSpy) directamente para estas cosas. Desde allí se puede ver que First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAty ElementAtOrDefaultestán optimizados para IList<TSource>, County Containsestán optimizados para ICollection<TSource>y Cast<TResult>está optimizado para IEnumerable<TResult>.
chillitom
8
asegúrese de agregarusing System.Linq;
Híbrido
44
@chillitom Los métodos de extensión expuestos por System.Linq.Enumerableno están realmente 'optimizados'. Aquí está el código para el Enumerable.Lastmétodo.
0b101010
44
@chillitom después de leer la fuente de System.Linq.Enumerable.Last, estoy de acuerdo con 0b101010, el Last()código no está "optimizado para List<>s", Last()es solo un contenedor feo, que por defecto es return list[list.Count-1]en caso de que el argumento sea IList, e itera sobre la lista hasta el final en caso de que no lo es ... lo que lo convierte en una solución muy pobre si IListes a LinkedList, ya que el indexador solo recorrerá la lista innecesariamente (no he encontrado una anulación iterando hacia atrás Item[]con index> Count / 2 en las fuentes de c #, YMMV )
20

Veamos la raíz de la pregunta, cómo abordar el último elemento de una Lista de forma segura ...

Asumiendo

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

Luego

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

"contar-1" es un mal hábito a menos que primero garantices que la lista no está vacía.

No hay una manera conveniente de verificar la lista vacía, excepto para hacerlo.

La forma más corta en la que puedo pensar es

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

podría hacer todo lo posible y hacer un delegado que siempre devuelva verdadero, y pasarlo a FindLast, que devolverá el último valor (o valor predeterminado construido si la lista está vacía). Esta función comienza al final de la lista, por lo que será Big O (1) o tiempo constante, a pesar de que el método normalmente es 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);

El método FindLast es feo si cuenta la parte de delegado, pero solo necesita declararse un lugar. Si la lista está vacía, devolverá un valor construido predeterminado del tipo de lista "" para cadena. Sería más útil llevar al delegado alwaysTrue un paso más, convirtiéndolo en una plantilla en lugar de un tipo de cadena.

JFDev
fuente
2
El delegado se puede reemplazar con una expresión lambda: myList.FindLast(_unused_variable_name => true);esto funcionará independientemente del tipo. Una versión más corta es myList.FindLast(_ => true);, pero creo que solo el guión bajo (o cualquier otro identificador de un solo carácter) puede ser un poco confuso a veces.
Bob
6
int lastInt = integerList[integerList.Count-1];
Dan Diplo
fuente
5

Cambio

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

a

for (int cnt3 = 0 ; cnt3 < integerList.Count; cnt3++)
Eric J.
fuente
foreach suele ser más conveniente de usar, pero es LIGERAMENTE más lento.
Eric J.
si usa Count ... haga un -1 o obtendrá un error de índice. for (int cnt3 = 0; cnt3 <integerList.Count - 1; cnt3 ++)
RiddlerDev el
44
Es por eso que cambié <= a <. El código es correcto según lo publicado :-)
Eric J.
@Eric: Solía ​​ser más lento, pero es un caso trivial en el JIT, por lo que me sorprendería si no lo han hecho hasta ahora. : no sé:
Sam Harwell
1
@IPX Ares: Parece que sigue siendo un problema, dependiendo del tipo de datos que está iterando: stackoverflow.com/questions/365615/…
Eric J.
2

Usa la Countpropiedad. El último índice será Count - 1.

for (int cnt3 = 0 ; cnt3 < integerList.Count; cnt3++)
Spencer Ruport
fuente
2

Puede encontrarlo contando primero el número de elementos en la lista, por ejemplo

int count = list.Count();

Luego puede indexar el conteo - 1 para obtener el último elemento en la lista, por ejemplo

int lastNumber = list[count - 1];
Muhammad Aasharib Nawshad
fuente
2
Por favor no publique respuestas duplicadas.
Ian Mercer
2

En C # 8.0 puede obtener el último elemento con ^ explicación completa del operador

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

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

¿Por qué no solo usar la propiedad Count en la lista?

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

Independientemente de su pregunta original, obtendrá un mejor rendimiento si captura referencias a variables locales en lugar de indexarlas en su lista varias veces:

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

Y en tu forbucle:

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
fuente
-1

Tendría que aceptar que un foreach sería mucho más fácil.

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

También le sugiero que agregue propiedades para acceder a su información en lugar de campos públicos, dependiendo de su versión .net puede agregarla public int MessageType {get; set;}y eliminarla de m_sus campos públicos, propiedades, etc., ya que no debería estar allí.

Michael Ciba
fuente
-1

Creo que esto te ayuda. por favor, compruebe

    TaxRangers[TaxRangers.Count]. max
arena arenosa
fuente