Está en la página 1de 16

Colecciones de datos .

NET
Danya Mara Justo Patio 04/05/2012

Colecciones de datos
En esta versin de Visual .NET, tenemos un amplio abanico de tipos de colecciones, desde colecciones genricas (o de uso comn, para no confundir el trmino con las colecciones "generic"), hasta colecciones especializadas, es decir, colecciones que estn pensadas para usarlas de forma muy concreta y que por tanto no nos sirven para usarlas en la mayora de las ocasiones. Empecemos viendo los tres tipos bsicos de colecciones que podemos utilizar en nuestras aplicaciones de .NET. Nota: Las colecciones que vamos a ver a continuacin son las colecciones "clsicas" de .NET, (cuyo tipo interno es Object), pero debemos saber que tambin existen colecciones casi con las mismas caractersticas pero que estn definidas en el espacio de nombres System.Collections.Generic, las cuales utilizan la nueva "tecnologa" de los tipos genricos (generic) para almacenar los elementos, (cuyo tipo interno puede ser de cualquier tipo). Los tipos de colecciones de .NET En .NET Framework existen tres tipos principales de colecciones, stas dependen del tipo de interfaz que implementan:

Las colecciones basadas en ICollection Las colecciones basadas en la interfaz IList Las colecciones basadas en la interfaz IDictionary

Como podemos imaginar, dependiendo del "contrato" firmado por cada una de estos tipos de colecciones, podremos hacer ciertas operaciones con ellas.A continuacin veremos con ms detalle estos tipos de colecciones y cules son las que podemos usar dependiendo del interfaz que cada una de ellas implemente. Nota: La diferencia bsica entre estos tipos de colecciones es cmo estn almacenados los elementos que contienen, por ejemplo, las colecciones de tipo IList (y las directamente derivadas de ICollection) solo almacenan un valor, mientras que las colecciones de tipo IDictionary guardan un valor y una clave relacionada con dicho valor. Tambin veremos unas clases base que implementan cada una de estas dos interfaces, las cuales las podemos usar como base de nuestras propias colecciones personalizadas.

Las colecciones basadas en ICollection La interfaz ICollection es un caso aparte, ya que realmente todas las colecciones de .NET implementan esta interfaz, de hecho, esta interfaz se deriva de IEnumerable que es la que nos permite recorrer las colecciones usando bucles foreach. Esta interfaz no la tendremos que usar de forma habitual, ya que realmente el resto de colecciones (e interfaces) tiles ya se derivan de ella, por tanto vamos a centrarnos en las otras dos. Aunque es importante que tengamos en cuenta que el resto de colecciones implementan ICollection, por tanto siempre podremos usar un objeto de este tipo para acceder a cualquier coleccin. Independientemente de que todas las colecciones de .NET estn basadas en esta interfaz, hay ciertos tipos de colecciones que solo implementan esta interfaz, por ejemplo las colecciones de tipo Queue, Stack o BitArray, por tanto esas colecciones estarn limitadas a los mtodos expuestos por la interfaz ICollection y los que esas colecciones implementen de forma independiente. Por regla general, los tipos que solo se basan en ICollection suelen ser colecciones que no necesitan de las caractersticas que proporcionan las otras interfaces y, por regla general, nos permiten la manipulacin de los datos de una forma bsica o elemental, de forma que su uso sea para casos muy concretos. Por ejemplo, la clase Queue nos permite crear fcilmente una coleccin de tipo FIFO (primero en entrar, primero en salir); por otra parte, con la clase Stack podemos crear colecciones del tipo LIFO (ltimo en entrar, el primero en salir), de forma que sean muy tiles para crear "pilas" de datos. El caso de la otra clase que hemos comentado: BitArray, nos sirve para almacenar valores de tipo "bit", en el que cada valor se almacena como un cero o un uno, de forma que podamos tener una coleccin muy compacta, pero, tambin muy especfica y no de uso general, ya que en este caso particular, los mtodos que implementa esta clase estn enfocados en la manipulacin de valores de tipo Boolean, (False y True), aunque internamente se almacenen como valores cero y uno respectivamente. Esta seccin contiene dos ejemplos de cdigo. En el primer ejemplo se muestra varias propiedades y mtodos de la clase Collection. En el segundo, se muestra cmo derivar una clase de coleccin de un tipo construido de Collection, y cmo reemplazar los mtodos protegidos de Collection para proporcionar un comportamiento personalizado. Ejemplo 1

En el ejemplo de cdigo siguiente se muestran muchos mtodos y propiedades de Collection. En este ejemplo de cdigo se crea una coleccin de cadenas, se utiliza el mtodo Add para agregar varias cadenas, se muestra Count y se confecciona una lista de las cadenas. Tambin se utiliza el mtodo IndexOf para localizar el ndice de una cadena y el mtodo Contains para determinar si una cadena se encuentra en la coleccin. En el ejemplo se inserta una cadena mediante el mtodo Insert y se recuperan y establecen cadenas mediante la propiedad Item predeterminada (el indizador en C#). Se quitan cadenas a partir de la identidad de cadena, utilizando el mtodo Remove, y a partir del ndice, utilizando el mtodo RemoveAt. Por ltimo, se usa el mtodo Clear para borrar todas las cadenas de la coleccin.
Imports System Imports System.Collections.Generic Imports System.Collections.ObjectModel Module Module1 Public Sub Main() Dim dinosaurs As New Collection(Of String) dinosaurs.Add("Psitticosaurus") dinosaurs.Add("Caudipteryx") dinosaurs.Add("Compsognathus") dinosaurs.Add("Muttaburrasaurus") Console.WriteLine("{0} dinosaurs:", dinosaurs.Count) Display(dinosaurs) Console.WriteLine(vbLf & "IndexOf(""Muttaburrasaurus""): {0}", _ dinosaurs.IndexOf("Muttaburrasaurus")) Console.WriteLine(vbLf & "Contains(""Caudipteryx""): {0}", _ dinosaurs.Contains("Caudipteryx")) Console.WriteLine(vbLf & "Insert(2, ""Nanotyrannus"")") dinosaurs.Insert(2, "Nanotyrannus") Display(dinosaurs) Console.WriteLine(vbLf & "dinosaurs(2): {0}", dinosaurs(2)) Console.WriteLine(vbLf & "dinosaurs(2) = ""Microraptor""") dinosaurs(2) = "Microraptor" Display(dinosaurs) Console.WriteLine(vbLf & "Remove(""Microraptor"")") dinosaurs.Remove("Microraptor") Display(dinosaurs) Console.WriteLine(vbLf & "RemoveAt(0)") dinosaurs.RemoveAt(0) Display(dinosaurs)

Console.WriteLine(vbLf & "dinosaurs.Clear()") dinosaurs.Clear() Console.WriteLine("Count: {0}", dinosaurs.Count) Console.ReadLine() End Sub Private Sub Display(ByVal cs As Collection(Of String)) Console.WriteLine() For Each item As String In cs Console.WriteLine(item) Next item End Sub End Module

Las colecciones basadas en IList


La interfaz IList se utiliza en las colecciones a las que queremos acceder mediante un ndice, por ejemplo, los arrays realmente est basados en esta interfaz, y tal como pudimos comprobar, la nica forma que tenemos de acceder a los elementos de un array, (y por extensin a los elementos de las colecciones basadas en IList), es mediante un ndice numrico. Existen tres tipos principales de colecciones que implementan esta interfaz: Las de solo lectura, colecciones que no se pueden modificar. Este tipo de colecciones suelen basarse en la clase abstracta ReadOnlyCollectionBase. Las colecciones de tamao fijo, no se pueden quitar ni aadir elementos, pero si modificarlos. Por ejemplo, las colecciones basadas en Array son de tamao fijo. Las de tamao variable permiten cualquier tipo de adicin, eliminacin y modificacin. La mayora de las colecciones suelen ser de este tipo, es decir, nos permiten dinmicamente aadir o eliminar elementos.

Existe un gran nmero de colecciones en .NET que implementan esta interfaz, (sobre todo las colecciones basadas en controles), entre las que podemos destacar las siguientes: ArrayList, la coleccin "clsica" para este tipo de interfaz. Contiene todos los miembros habituales en este tipo de colecciones. CollectionBase, una clase abstracta para poder crear nuestras propias colecciones basadas en IList. StringCollection, una coleccin especializada que solo puede contener valores de tipo cadena.

Descripcion IList es una descendiente de la interfaz ICollection y es la interfaz base de todas las listas no genricas. Las implementaciones de IList se dividen en tres categoras: de solo lectura, de tamao fijo y de tamao variable. Una interfaz IList de slo lectura no se puede modificar. Una interfaz IList de tamao fijo no permite agregar o eliminar elementos, pero s modificar elementos existentes. Una interfaz IList de tamao variable permite agregar, quitar y modificar elementos.

Para obtener la versin genrica de esta interfaz, vea System.Collections.Generic.IList(Of T)

Ejemplos
En el ejemplo siguiente, se muestra la implementacin de la interfaz IList para crear una lista simple, lista de tamao fijo.

Imports System Imports System.Collections Public Class Class1 End Class Module Module1 Sub main() End Sub Public Class Program Public Sub Main() Dim myList As New SimpleList() ' Populate the List Console.WriteLine("Populate the List") myList.Add("one") myList.Add("two") myList.Add("three") myList.Add("four") myList.Add("five") myList.Add("six") myList.Add("seven") myList.Add("eight") myList.PrintContents() Console.WriteLine() ' Remove elements from the list Console.WriteLine("Remove elements from the list") myList.Remove("six") myList.Remove("eight") myList.PrintContents() Console.WriteLine() ' Add an element to the end of the list Console.WriteLine("Add an element to the end of the list") myList.Add("nine") myList.PrintContents() Console.WriteLine() ' Insert an element into the middle of the list Console.WriteLine("Insert an element into the middle of the list")

myList.Insert(4, "number") myList.PrintContents() Console.WriteLine() ' Check for specific elements in the list Console.WriteLine("Check for specific elements in the list") Console.WriteLine("List contains 'three': {0}", myList.Contains("three")) Console.WriteLine("List contains 'ten': {0}", myList.Contains("ten")) Console.ReadLine() End Sub End Class ' class Program Public Class SimpleList Implements IList Private _contents(8) As Object Private _count As Integer Public Sub New() _count = 0 End Sub ' IList Members Public Function Add(ByVal value As Object) As Integer Implements IList.Add If (_count < _contents.Length - 1) Then _contents(_count) = value _count = _count + 1 Return (_count - 1) Else Return -1 End If End Function Public Sub Clear() Implements IList.Clear _count = 0 End Sub Public Function Contains(ByVal value As Object) As Boolean Implements IList.Contains Dim inList As Boolean = False Dim i As Integer For i = 0 To Count If _contents(i) = value Then inList = True

Exit For End If Next i Return inList End Function Public Function IndexOf(ByVal value As Object) As Integer Implements IList.IndexOf Dim itemIndex As Integer = -1 Dim i As Integer For i = 0 To Count If _contents(i) = value Then itemIndex = i Exit For End If Next i Return itemIndex End Function Public Sub Insert(ByVal index As Integer, ByVal value As Object) Implements IList.Insert If (_count + 1) <= (_contents.Length - 1) And (index < Count) And (index >= 0) Then _count = _count + 1 Dim i As Integer For i = Count - 1 To index _contents(i) = _contents(i - 1) Next i _contents(index) = value End If End Sub Public ReadOnly Property IsFixedSize() As Boolean Implements IList.IsFixedSize Get Return True End Get End Property Public ReadOnly Property IsReadOnly() As Boolean Implements IList.IsReadOnly

Get Return False End Get End Property Public Sub Remove(ByVal value As Object) Implements IList.Remove RemoveAt(IndexOf(value)) End Sub Public Sub RemoveAt(ByVal index As Integer) Implements IList.RemoveAt If index >= 0 And index < Count Then Dim i As Integer For i = index To Count - 1 _contents(i) = _contents(i + 1) Next i _count = _count - 1 End If End Sub Public Property Item(ByVal index As Integer) As Object Implements IList.Item Get Return _contents(index) End Get Set(ByVal value As Object) _contents(index) = value End Set End Property ' ICollection Members Public Sub CopyTo(ByVal array As Array, ByVal index As Integer) Implements ICollection.CopyTo Dim j As Integer = index Dim i As Integer For i = 0 To Count array.SetValue(_contents(i), j) j = j + 1 Next i End Sub Public ReadOnly Property Count() As Integer Implements ICollection.Count Get Return _count End Get End Property Public ReadOnly Property IsSynchronized() As Boolean Implements ICollection.IsSynchronized

Get Return False End Get End Property ' Return the current instance since the underlying store is not ' publicly available. Public ReadOnly Property SyncRoot() As Object Implements ICollection.SyncRoot Get Return Me End Get End Property ' IEnumerable Members Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator ' Refer to the IEnumerator documentation for an example of ' implementing an enumerator. Throw New Exception("The method or operation is not implemented.") End Function Public Sub PrintContents() Console.WriteLine("List has a capacity of {0} and currently has {1} elements.", _contents.Length - 1, _count) Console.Write("List contents:") Dim i As Integer For i = 0 To Count Console.Write(" {0}", _contents(i)) Next i Console.WriteLine() End Sub End Class ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' This code produces output similar to the following: Populate the List: List has a capacity of 8 and currently has 8 elements. List contents: one two three four five six seven eight Remove elements from the list: List has a capacity of 8 and currently has 6 elements. List contents: one two three four five seven Add an element to the end of the list: List has a capacity of 8 and currently has 7 elements. List contents: one two three four five seven nine Insert an element into the middle of the list: List has a capacity of 8 and currently has 8 elements. List contents: one two three four number five seven nine

' Check for specific elements in the list: ' List contains "three": True ' List contains "ten": False 'End Class 'End Sub End Module La coleccin ArrayList Tal como hemos comentado, el tipo de coleccin que se usa como referencia a la hora de hablar de las colecciones basadas en la interfaz IList, es ArrayList. Esta coleccin permite aadir, eliminar y modificar fcilmente los elementos que contiene. Tambin podemos recorrerlos mediante un bucle for accediendo a los elementos por medio de un ndice e incluso mediante un bucle del tipo foreach. Al igual que ocurre con los arrays, el ndice inferior es siempre el cero y los elementos se almacenan de forma consecutiva, es decir, si aadimos dos elementos a una coleccin de tipoArrayList (y a las que implementen la interfaz IList), el primero ocupar la posicin cero y el segundo la posicin uno. La ventaja de trabajar con las colecciones es que no debemos preocuparnos de reservar memoria cada vez que vayamos a aadir un nuevo elemento, simplemente usamos el mtodo Add y asunto arreglado. Lo mismo ocurre a la hora de quitar elementos de una coleccin, no tenemos que preocuparnos demasiado por el espacio dejado al quitar elementos, de eso se encarga el propio .NET, nosotros simplemente debemos llamar al mtodo Remove o RemoveAt indicando respectivamente el elemento a eliminar o el ndice en el que se encuentra almacenado.

No se garantiza que la matriz ArrayList est ordenada. Debe ordenar la matriz ArrayList antes de realizar operaciones (como BinarySearch) que requieren que la matrizArrayList est ordenada. La capacidad de un objeto ArrayList es el nmero de elementos que puede contener el objeto ArrayList. La capacidad inicial predeterminada de un objeto ArrayList es 0. Conforme se agregan elementos a un objeto ArrayList, la capacidad aumenta automticamente segn lo requiera la reasignacin. Se puede disminuir la capacidad llamando al mtodo TrimToSize o estableciendo explcitamente la propiedad Capacity. Se puede obtener acceso a los elementos de esta coleccin utilizando un ndice entero. Los ndices de esta coleccin estn basados en cero. El objeto ArrayList acepta referencia de objeto null (Nothing en Visual Basic) como valor vlido y permite elementos duplicados.

Ejemplo
En el ejemplo de cdigo siguiente se muestra cmo se crea e inicializa un objeto ArrayList y cmo se imprimen sus valores. Imports System Imports System.Collections Imports Microsoft.VisualBasic Module Module1 Public Sub Main() ' Creates and initializes a new ArrayList. Dim myAL As New ArrayList() myAL.Add("Hello") myAL.Add("World") myAL.Add("!") ' Displays the properties and values of the ArrayList. Console.WriteLine("myAL") Console.WriteLine(" Count: {0}", myAL.Count) Console.WriteLine(" Capacity: {0}", myAL.Capacity) Console.Write(" Values:") PrintValues(myAL) Console.ReadLine() End Sub Public Sub PrintValues(ByVal myList As IEnumerable) Dim obj As [Object] For Each obj In myList Console.Write(" {0}", obj) Next obj Console.WriteLine() End Sub 'PrintValues 'End Class ' This code produces output similar to the following: ' ' myAL ' Count: 3 ' Capacity: 4 ' Values: Hello World !

'End Sub End Module

Las colecciones basadas en IDictionary


El otro grupo de colecciones que podemos encontrar en .NET son las colecciones basadas en la interfaz IDictionary. stas, a diferencia de las colecciones IList, siempre mantienen el par clave/valor, ya que la forma de acceder a los elementos es mediante una clave nica. Por tanto, cada vez que aadimos un elemento a una coleccin de este tipo tendremos que indicar una clave y un valor. Cada valor estar relacionado con su correspondiente clave. Sabiendo esto, es fcil adivinar que si queremos acceder a un elemento, lo normal es que lo hagamos usando la clave indicada al aadirlo. En las colecciones basadas en IDictionary, salvo casos muy especiales, siempre accederemos a los valores contenidos mediante la clave y "nunca" mediante un ndice que haga referencia a la posicin dentro de la coleccin, entre otras cosas porque cuando almacenamos valores en este tipo de colecciones, stos no se guardan en el mismo orden en que fueron aadidos. Entre las colecciones basadas en la interfaz IDictionary podemos destacar: Hashtable, es la coleccin por excelencia de las basadas en IDictionary. Los elementos se organizan basndose en el cdigo hash de las claves. DictionaryBase, es una clase abstracta que podemos usar como base de nuestras propias colecciones de tipo diccionario. ListDictionary, es una coleccin con mayor rendimiento que Hashtable pensada para trabajar con 10 o menos elementos. HybridDictionary, es una coleccin especial en la que si hay 10 o menos elementos, se utiliza una coleccin ListDictionary y si contiene ms elementos se utiliza una coleccinHashtable. SortedList, es una coleccin en la que los elementos estn clasificados por las claves. Internamente utiliza una mezcla entre Hashtable y Array, segn la forma en que se accedan a esos elementos.

Sintaxis
'Declaration <ComVisibleAttribute(True)> _ Public Interface IDictionary Inherits ICollection, IEnumerable 'Usage Dim instance As IDictionary En el siguiente ejemplo de cdigo se muestra cmo definir una clase de diccionario simple que implementa la interfaz IDictionary. ' This class implements a simple dictionary using an array of DictionaryEntry objects (key/value pairs). Public Class SimpleDictionary Implements IDictionary ' The array of items Dim items() As DictionaryEntry Dim ItemsInUse As Integer = 0 ' Construct the SimpleDictionary with the desired number of items.

' The number of items cannot change for the life time of this SimpleDictionary. Public Sub New(ByVal numItems As Integer) items = New DictionaryEntry(numItems - 1) {} End Sub ' IDictionary Members Public ReadOnly Property IsReadOnly() As Boolean Implements IDictionary.IsReadOnly Get Return False End Get End Property Public Function Contains(ByVal key As Object) As Boolean Implements IDictionary.Contains Dim index As Integer Return TryGetIndexOfKey(key, index) End Function Public ReadOnly Property IsFixedSize() As Boolean Implements IDictionary.IsFixedSize Get Return False End Get End Property Public Sub Remove(ByVal key As Object) Implements IDictionary.Remove If key = Nothing Then Throw New ArgumentNullException("key") End If ' Try to find the key in the DictionaryEntry array Dim index As Integer If TryGetIndexOfKey(key, index) Then ' If the key is found, slide all the items up. Array.Copy(items, index + 1, items, index, (ItemsInUse - index) - 1) ItemsInUse = ItemsInUse - 1 Else ' If the key is not in the dictionary, just return. End If End Sub Public Sub Clear() Implements IDictionary.Clear ItemsInUse = 0 End Sub Public Sub Add(ByVal key As Object, ByVal value As Object) Implements IDictionary.Add ' Add the new key/value pair even if this key already exists in the dictionary. If ItemsInUse = items.Length Then Throw New InvalidOperationException("The dictionary cannot hold any more items.") End If items(ItemsInUse) = New DictionaryEntry(key, value) ItemsInUse = ItemsInUse + 1 End Sub Public ReadOnly Property Keys() As ICollection Implements IDictionary.Keys Get ' Return an array where each item is a key. Dim keyArray() As Object = New Object(ItemsInUse - 1) {}

Dim n As Integer For n = 0 To ItemsInUse - 1 keyArray(n) = items(n).Key Next n Return keyArray End Get End Property Public ReadOnly Property Values() As ICollection Implements IDictionary.Values Get ' Return an array where each item is a value. Dim valueArray() As Object = New Object(ItemsInUse - 1) {} Dim n As Integer For n = 0 To ItemsInUse - 1 valueArray(n) = items(n).Value Next n Return valueArray End Get End Property Public Property Item(ByVal key As Object) As Object Implements IDictionary.Item Get ' If this key is in the dictionary, return its value. Dim index As Integer If TryGetIndexOfKey(key, index) Then ' The key was found return its value. Return items(index).Value Else ' The key was not found return null. Return Nothing End If End Get Set(ByVal value As Object) ' If this key is in the dictionary, change its value. Dim index As Integer If TryGetIndexOfKey(key, index) Then ' The key was found change its value. items(index).Value = value Else ' This key is not in the dictionary add this key/value pair. Add(key, value) End If End Set End Property Private Function TryGetIndexOfKey(ByVal key As Object, ByRef index As Integer) As Boolean For index = 0 To ItemsInUse - 1 ' If the key is found, return true (the index is also returned). If items(index).Key.Equals(key) Then Return True End If Next index

' Key not found, return false (index should be ignored by the caller). Return False End Function

Cmo se almacenan los elementos de las colecciones IDictionary Tal como hemos comentado, las colecciones que implementan la interfaz IDictionary siempre almacenan un par de datos: la clave y el valor propiamente dicho, por tanto cada vez que queramos acceder a un valor, debemos usar la clave asociada con dicho valor. Al menos esa es la forma habitual, ya que como veremos, tambin podremos acceder a esos valores directamente. Debido a esta caracterstica, para acceder a los elementos de este tipo de colecciones por medio de un bucle del tipo foreach, debemos usar una clase llamada DictionaryEntry, esta clase tiene dos propiedades, una contiene la clave y otra el valor. Por tanto, cuando usemos un bucle foreach, el tipo de objeto usado para acceder a los elementos de la coleccin serDictionaryEntry, tal como vemos en el siguiente cdigo:
foreach (DictionaryEntry de in dic) Console.WriteLine("{0} = {1}", de.Key, de.Value);

Obtener todas las claves y valores de una coleccin IDictionary Independientemente de que podamos recorrer los valores contenidos en una coleccin de tipo IDictionary usando un objeto de tipo DictionaryEntry, habr ocasiones en las que realmente nos interesen tener solo los valores e incluso solo las claves, en estos casos, podemos usar dos propiedades que la interfaz IDictionary define: Keys y Values. Estas propiedades devuelven un objeto del tipo ICollection con las claves y valores respectivamente. Al ser objetos ICollection, solo podremos usarlos para recorrerlos por medio de un bucle foreach, ya que las colecciones ICollection no tienen ningn mtodo que nos permita acceder a los elementos que contiene usando un ndice. En el siguiente cdigo mostramos todas las claves de la coleccin creada en el ejemplo anterior:
foreach (string clave in dic.Keys) Console.WriteLine(clave);

También podría gustarte