Tips & Tricks | Reemplazar un elemento de una lista en C#

En un artículo reciente os hablé de cómo unir dos listas de elementos en C# con Linq eliminando los duplicados y en este artículo os traigo una pequeño tip muy simple que es cómo reemplazar un elemento de una lista.

Contexto

Supongamos que tenemos una lista de elementos y que en un momento dado queremos reemplazar uno de ellos por un elemento actualizado. A priori, tenemos varias opciones para realizar esta labor, unas mejores que otras pero en este artículo hablaré de 3 de ellas.

Actualizar el valor de las propiedades del objeto existente

La primera opción, que es la que generalmente se suele ver en desarrolladores con poca experiencia, es la de obtener el objeto de la lista y actualizarle los valores uno por uno, pero no es ni mucho menos una opción adecuada o al menos no lo es en la gran mayoría de los casos.

Esta operación tiene en su contra el pobre rendimiento y que, al tener que actualizar los valores uno por uno, hay que especificar de forma explícita las propiedades con lo que perdemos la flexibilidad ante los cambios, esto es, si se añade una nueva propiedad a la clase de la que hemos construido la lista, habrá que revisar el código para actualizar aquellos métodos que realicen la labor de actualizar los valores de las propiedades, algo que se antoja inmantenible.

Si vamos un poco más allá, se pueden plantear variantes usando Reflexión o librerías de terceros como AutoMapper, pero se incluye al menos la variable del pobre rendimiento.

Así pues, esta opción, aunque suele ser de las primeras en aparecer por las mentes, no es la más adecuada cuando estamos hablando de aplicaciones mínimamente complejas y que tratan una cantidad de datos meridianamente grande.

Eliminar el elemento original y añadir el nuevo

Una opción más adecuada consiste en buscar el elemento original, eliminarlo de la lista y, finalmente, insertar el nuevo. Esta a priori es una buena solución bajo mi punto de vista porque es bastante rápida y es flexible a cambios en el modelo.

En la siguiente muestra de código podréis observar cómo, en tres líneas podemos tener esta operación que se compone de tres partes, Búsqueda, Borrado, Inserción.

List<Saint> list = new List<Saint>
{
    new Saint { Id=1, Name="Seiya", Constellation="Pegasus", Description="Bronze Saint" },
    new Saint { Id=6, Name="Dohko", Constellation="Libra", Description="Gold Saint"  },
    new Saint { Id=7, Name="Shaka", Constellation="Virgo", Description="Gold Saint"  },
    new Saint { Id=5, Name="Ikki", Constellation="Phoenix", Description="Bronze Saint Original" }
            };
var replaceItem = new Saint { Id = 5, Name = "Ikki", Constellation = "Phoenix", Description = "Bronze Saint Replaced" };

var element = list.FirstOrDefault(i => i.Id == replaceItem.Id);
list.Remove(element);
list.Add(replaceItem);

Como se ve en la ejecución del ejemplo, el resultado es la misma lista con el elemento actualizado y la ejecución ha tardado 0.7515ms con lo que es un tiempo bastante razonable.

Pensad siempre que en situaciones reales puede que no estemos actualizando únicamente 1 elemento, sino que en ocasiones podrían llegar a ser cientos o miles, teniendo que multiplicar este valor por ese número de elementos.

Buscar el índice y reemplazar el elemento

Por último, una opción algo más eficiente, consiste en buscar el elemento sin obtenerlo, es decir, sólo recibir el índice de su posición en la lista y asignar a ese índice el elemento actualizado con lo que, en definitiva, estaríamos realizando una operación menos que en el caso anterior, Búsqueda, Asignación

var elementIndex = list.FindIndex(i => i.Id == replaceItem.Id);
list[elementIndex] = replaceItem;

Como se puede observar, aunque los tiempos de ejecución varían algo cada vez que se realiza la operación, en este caso he obtenido un tiempo que es menor a un 15% del anterior método, siendo de 0.0447ms

Conclusión

Cuando desarrollamos aplicaciones y tenemos que resolver pequeños problemas, debemos pensar en el rendimiento de nuestras soluciones porque a veces, si estás se ejecutan de forma masiva, pueden tener un impacto notable en este. En este artículo hemos visto cómo entre soluciones que a priori parecen buenas puede existir una gran diferencia y esta marcar el funcionamiento final de la aplicación.

Espero que os sirva de ayuda este pequeño Tip

Enjoy coding!