Menu

List en C#: Add, Remove, Contains, Find y Sort

List<T> es el array que crece de C#. Aprende a crear una lista, añadir e insertar elementos, eliminar por valor, por índice o por condición, buscar con Contains y Find, ordenar por una propiedad y evitar el error de cambiar una lista dentro de un foreach.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

List<T> es una colección ordenada que crece y se reduce a medida que añades y quitas elementos. Vive en System.Collections.Generic, se indexa como un array (list[0]) y es la opción por defecto para "un montón de cosas" en C#.

Crear una lista y añadir elementos

El parámetro de tipo entre corchetes angulares dice qué contiene la lista: List<string>, List<int>, List<Order>.

Salida:

2
Zoe, Ana, Ben, Chloe, Dev
Ana
Ana B.
3

Count es una propiedad, no un método (los arrays usan Length y LINQ tiene un método Count(); en una lista usa la propiedad). Insert desplaza una posición todos los elementos posteriores, así que insertar al principio de una lista grande cuesta un tiempo proporcional a su tamaño. Add al final es la operación barata.

Eliminar elementos

Hay cuatro formas de eliminar, según lo que sepas del elemento:

Salida:

True: 85, 62, 40, 91, 55
False
62, 40, 91, 55
2 removed: 62, 91
Caught ArgumentOutOfRangeException
0

Remove recibe un valor y borra solo la primera coincidencia. Para borrar todas las copias de un valor, usa RemoveAll(s => s == 40). Un índice incorrecto en una lista lanza ArgumentOutOfRangeException (un array lanza IndexOutOfRangeException), tanto con list[i] como con RemoveAt(i). RemoveRange(index, count) elimina un bloque.

Count frente a Capacity

Una lista guarda sus elementos en un array interno. Capacity es el tamaño de ese array; Count es cuántas posiciones están ocupadas. Cuando Count llega a Capacity, el siguiente Add reserva un array del doble de tamaño y copia todo.

Salida:

Count 0, Capacity 0
Count 1, Capacity 4
Count 2, Capacity 4
Count 3, Capacity 4
Count 4, Capacity 4
Count 5, Capacity 8
Count 6, Capacity 8
Count 7, Capacity 8
Count 8, Capacity 8
Count 9, Capacity 16
Count 0, Capacity 1000

Duplicar significa que la mayoría de las llamadas a Add son solo una escritura en una posición libre. Si sabes más o menos cuántos elementos van a llegar, pasa ese número al constructor para ahorrarte las copias intermedias. Ten en cuenta que new List<int>(1000) sigue teniendo Count 0: list[0] sobre ella lanza una excepción. La capacidad es espacio, no elementos.

Buscar: Contains, IndexOf, Find, Exists

Salida:

True
2
-1
32.00
2 cheap items
False
2
True
False

Find devuelve la primera coincidencia o el valor por defecto del tipo (null en una clase) cuando nada coincide, así que comprueba el resultado antes de usarlo. La última línea imprime False porque Contains usa Equals, y una clase sin una sobrescritura de Equals compara referencias: un Product nuevo con los mismos campos es un objeto distinto. Busca por una propiedad con Exists o Find, o sobrescribe Equals y GetHashCode en la clase.

Todos estos métodos recorren la lista desde el principio, así que cada llamada es O(n). Para búsquedas repetidas por una clave, un Dictionary responde en tiempo constante.

Ordenar una lista

Sort() ordena en el sitio usando el orden natural de los elementos. Pasa una lambda de comparación para ordenar por cualquier otra cosa.

Salida:

1, 2, 5, 9
9, 5, 2, 1
Ben 95, Ana 120, Chloe 150
Chloe 150, Ben 95, Ana 120
Ben 95, Ana 120, Chloe 150

Una comparación devuelve un número negativo cuando a debe ir primero, positivo cuando debe ir b, y cero cuando empatan; CompareTo produce exactamente eso. Dos diferencias entre Sort y el OrderBy de LINQ importan en la práctica:

  • Sort cambia la lista; OrderBy devuelve una secuencia nueva y deja la lista como estaba.
  • Sort no es estable: los elementos que se comparan como iguales pueden intercambiarse. OrderBy es estable, y ThenBy añade una segunda clave: players.OrderBy(p => p.Score).ThenBy(p => p.Name).

Llamar a Sort() sobre una lista de una clase tuya sin una comparación lanza InvalidOperationException, porque la lista no sabe cómo ordenar los elementos. Pasa una comparación o implementa IComparable<T> en la clase.

Reverse() invierte en el sitio. Como System.Linq también define una extensión Reverse, list.Reverse() sobre una List<T> elige la versión en el sitio, que no devuelve nada.

Recorrer, y eliminar mientras se itera

foreach lee todos los elementos en orden. Un bucle for te da también el índice. Lo que no puedes hacer es añadir o quitar elementos en mitad de un foreach sobre la misma lista:

Salida:

Caught InvalidOperationException
120, 80, 60
120, 80, 60

La excepción es InvalidOperationException con el mensaje "Collection was modified; enumeration operation may not execute." Un bucle for hacia delante no lanza excepción, pero se salta en silencio el elemento que va justo después de cada uno que eliminas, porque todo se desplaza un índice hacia abajo. Recorrer hacia atrás lo evita. Una tercera solución es iterar sobre una copia: foreach (var t in orders.ToList()).

Cambiar una propiedad de un elemento dentro de foreach (por ejemplo order.Status = "sent") no da problemas. Añadir o quitar elementos rompe el enumerador, y también sustituir uno a través del indexador (orders[i] = 0).

Convertir entre listas y arrays

Salida:

4
3
4, 4, 4, 4
Oslo / Lima / Pune / Kyiv

Las dos direcciones copian los elementos en un almacenamiento nuevo, así que cambiar la lista después no afecta al array. ConvertAll es la versión propia de la lista del Select(...).ToList() de LINQ. Imprimir una lista con Console.WriteLine(list) muestra el nombre del tipo (System.Collections.Generic.List`1[System.String]); usa string.Join.

Referencia rápida

TareaCódigo
Crearvar l = new List<int>(); o new List<int> { 1, 2 }
Añadir al finall.Add(x), l.AddRange(items)
Insertar en una posiciónl.Insert(i, x)
Eliminar la primera coincidencial.Remove(x) (devuelve bool)
Eliminar por índicel.RemoveAt(i)
Eliminar por condiciónl.RemoveAll(x => ...) (devuelve el número)
Número de elementosl.Count
Contienel.Contains(x), l.Exists(x => ...)
Buscarl.Find(...), l.FindAll(...), l.FindIndex(...)
Ordenar en el sitiol.Sort(), l.Sort((a, b) => ...)
Copia ordenadal.OrderBy(x => ...).ToList()
A arrayl.ToArray()

Errores comunes

  • Eliminar dentro de foreach. Lanza InvalidOperationException; usa RemoveAll o un bucle for hacia atrás.
  • Esperar que Remove(x) elimine todas las copias. Elimina la primera; usa RemoveAll.
  • Usar Contains con objetos sin Equals. Compara referencias; busca por propiedad con Exists.
  • Indexar una lista creada con una capacidad. new List<int>(10) está vacía; añade elementos primero.
  • Suponer que Sort mantiene el orden de los empates. No lo hace; usa OrderBy cuando importa el orden entre iguales.
  • Asignar una lista para compartirla y sorprenderse por los cambios. var copy = list; es la misma lista; new List<T>(list) es una copia.

Preguntas frecuentes

¿Cómo añado elementos a una List en C#?

list.Add(item) añade un elemento al final, list.AddRange(otherCollection) añade muchos y list.Insert(index, item) coloca un elemento en una posición y desplaza el resto. También puedes llenar una lista al crearla: var names = new List<string> { "Ana", "Ben" };.

¿Cómo elimino un elemento de una List en C#?

Remove(value) elimina el primer elemento que coincide y devuelve true si lo encontró. RemoveAt(index) elimina por posición. RemoveAll(x => condition) elimina todos los elementos que cumplen la condición y devuelve cuántos eliminó. Clear() vacía la lista.

¿Cómo compruebo si una List contiene un valor en C#?

list.Contains(value) devuelve true o false. Compara con Equals, así que con tus propias clases compara referencias salvo que la clase sobrescriba Equals. Para comprobar por una propiedad, usa list.Exists(p => p.Name == "Ana") o list.Any(...) de LINQ.

¿Cómo ordeno una List por una propiedad en C#?

Pasa una comparación a Sort: people.Sort((a, b) => a.Age.CompareTo(b.Age)) ordena en el sitio. Intercambia a y b para el orden descendente. O usa LINQ, que devuelve una secuencia ordenada nueva y mantiene los elementos iguales en su orden original: people.OrderBy(p => p.Age).ToList().

¿Por qué obtengo "Collection was modified; enumeration operation may not execute"?

Añadiste, eliminaste o sustituiste (list[i] = x) elementos mientras un foreach recorría la misma lista, y el enumerador lanza InvalidOperationException en su siguiente paso. Usa list.RemoveAll(condition), recorre hacia atrás con un bucle for o itera sobre una copia como list.ToList().

¿Qué diferencia hay entre Count y Capacity?

Count es el número de elementos de la lista. Capacity es el tamaño del array interno, que es al menos Count. Cuando el array se llena, la lista reserva uno nuevo del doble de tamaño y copia los elementos, así que Add es rápido de media.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR