Colecciones en .NET 2 (mención especial NameValueCollection)

Vamos a darle un repaso a las colecciones elemento fundamental para programar, entre otras cosas, ya que los DataTable o DataSet cada día está más en desuso por su coste en recursos frente a las colecciones.

Vamos a ver los pros y los contras de las colecciones más famosas, y al final haremos una crítica al uso y abuso de NameValueCollection que me ha llamado la atención.

Gracias a Dino Esposito,gran divulgador de .NET, echemos un vistazo a la oferta de colecciones que nos proporciona .NET 2:

colecciones

Interfaces de las colecciones

Antes de meternos con las clases, hay que remontarse a los interfaces que se encuentran en el namespace System.Collection.Generic y de las que heredan, cosa que resulta fundamental para después saber qué métodos podemos implementar o qué colecciones podemos pasar a un determinado DataSource.

Tenemos 3 interfaces fundamentales para las colecciones: ICollection, IList e IDictionary.

Estas 3 heredan de IEnumerable.

IList y IDictionary heredan a su vez de ICollection.

Lo más destacable de estos 3 interfaces:

1.-ICollection:

De este interfaz, obtenemos los siguientes métodos: Count, CopyTo(), GetEnumerator() (para iterar la colección).

También tenemos el método IsSynchronized() y Synchronize() para aislar el objeto en una aplicación multihilo.

Las clases que lo implementan directamente son por ejemplo: Queue, Stack .

Estas 2 clases son curiosas porque sus elementos se destruyen después de acceder a sus datos, la diferencia es que Queue es un contenedor first-in first-out o sea el primero que pusimos es el primero al que accedemos y en el Stack es al contrario.

El problema con las colecciones que sólo heredan de ICollection es que no contemplan métodos para quitar o añadir elementos como pasa con IList.

2.-IList:

Implementa ICollection y permite añadir y quitar elementos (Add / Insert / Remove / RemoveAt). También incluye: Clear(), Contains().

Clases que heredan de IList: Array, ArrayList,DataView y CollectionBase.

3.-IDictionary: tipo de colección (implementa ICollection) diferente a las anteriores, porque son colecciones de pares de clave y valor (inglés:"collection of key/value pairs").

Incluye el método: ContainsKey() y TryGetValue() .

Los diccionarios almacenan los pares en KeyValuePairs , entonces para iterarlos podemos hacer lo siguiente:

Dictionary<string, string> 
dic = new Dictionary<string, string="">();
dic.Add("pruebaclave", "prueba"); 
foreach (KeyValuePair<string, string> kvp in dic) { 
Console.WriteLine("key={0},value={1}", kvp.Key,kvp.Value);
}

Si no funciona KeyValuePair, probad DictionaryEntry (por ejemplo: ListDictionary).

Las clases más típicas que implementan IDictionary: Hashtable, y SortedList.

SortedList es una clase con más overhead, ya que cada vez que añades elemento hace una búsqueda binaria para ordenarlo.

El problema con Hashtable es que no preserva el orden de añadido de ítems.

Clases Especializadas

Las clases que he mencionado desde el comienzo del artículo (y que implementan estos interfaces) pertenecen al espacio de nombre System.Collections en su mayoría.

Son clases muy generales que aceptan todo tipo de variables, o sea, reciben objetos / Objects, no están fuertemente tipados y en el caso de los diccionarios sólo manejan pares de clave y valor simétricos.

Su flexibilidad de uso contrasta con el coste / overhead que produce por el continuo boxing y unboxingEste enlace abre una nueva ventana que debemos realizar.

Para echarnos un cable existen 2 recursos:

1.- las clases genéricas en System.Collections.Generics (List<string>, Dictionary<string,string>)

2.-las clases que nos provee otro espacio de nombres: System.Collection.Specialized.

Atentos, estas clases especializadas contienen un overhead: internamente algunas de ellas implementan un ArrayList o HashTable.

En este namespace, encontramos por ejemplo:

- StringCollection: Es un ArrayList para strings, genial para colecciones de cadenas. Carencias: no tiene Sort al contrario que ArrayList o List.

- StringDictionary: diccionario de cadenas, pero no guarda el orden de añadido y además hace lowercase a la clave.

- ListDictionary: es un diccionario más rápido que un Hashtable para menos de 10 items

- HybridDictionary: para mejorar el rendimiento, es una lista que empieza como ListDictionary hasta los 15 items y después cambia a Hashtable aunque pierde el orden de añadido.

- OrderedDictionary: Mejor que Dictionary siempre que necesites acceder a los datos por índice ( lista[0] ). Imposible con Dictionary, HashTable, StringDictionary etc...

- NameValueCollection: es un diccionario especial porque puede gestionar varios valores para una clave. Si yo añado 2 claves iguales, añadirá los valores separados por comas a la misma clave. Esto es un diccionario asimétrico. Además se puede acceder por índice.

Clases genéricas / Clases propias

De todas las colecciones yo elegiría a poder ser las genéricas, por ejemplo: List<string>, Dictionary<string,string> bastante mejores que ArrayList.

No obstante, la solución más elegante siempre será crear nuestras propias colecciones propias de los objetos que estamos tratando para mayor claridad y mejor refactorización.

Un snippet rápido para hacerlo sería:

public class ActividadCollection : List<Actividad> { }

Nos ahorramos programar los métodos Add / Remove etc...aunque también puedes implementarlos heredando de las clases abstractas: CollectionBase, DictionaryBase, ReadonlyCollectionBase y NameObjectCollectionBase. Tiene menos overhead pero el trabajo no merece la pena.

NameValueCollection

Aquí dejamos el repaso a las colecciones, y hablemos de NameValueCollection.

Esta colección NameValueCollection aunque por su nombre parezca una colección muy sencilla, tiene un coste mayor que los otros diccionarios. Motivo: implementa un HashTable internamente.

Aquí estudian su rendimiento y la conclusión es que es más óptimo un StringDictionary o un genérico Dictionary<string,string> a un NameValueCollection siempre que no queramos asociar varios valores a una clave.

Elegir colección

Debemos escoger la que proporcione mejor rendimiento, aunque debemos pensar en la mayor escalabilidad también. Mi recomendación es que empieces mirando las clases más simples y vayas subiendo a las más complejas hasta hallar la colección idónea.

Escrito el 11 ene 2009
comments powered by Disqus

« Ordenando un StringCollection: ArrayList.Adapter - Subdominios, dominios y sesiones »