miércoles, 11 de septiembre de 2013

Arreglos en C#

Tabla de Contenido

0. Introducción
1. ¿Qué es un Arreglo?
2. ¿Cómo Declarar un Arreglo?
3. Declaración de Arreglos
4. Inicialización de Arreglos
5. Acceso a los Miembros de un Arreglo
6. Todos los Arreglos son Objetos
7. Uso de la Construcción foreach en Arreglos
8. Chequeo de Límites de Arreglos
9. Conclusiones
10. Glosario
11. Referencias

0. Introducción

Una de las estructuras de datos más básicas que puede contener un lenguaje de programación moderno (C++, Java, PHP, Python, VB.NET, C#, la lista es interminable) son los arreglos. En este artículo les voy a presentar los detalles generales de esta construcción que nos hace la vida más fácil cuando queremos manipular grupos de variables (elementos) y valores asociados a ellas.

1. ¿Qué es un Arreglo?

Parto de la definición en [1] (que me parece muy breve e interesante): podemos ver a un arreglo como un conjunto de variables (de un tipo en particular). Visto que se trata de un conjunto, los elementos en un arreglo son almacenados en bloques de memoria contiguos (esto provee una ventaja en uso de memoria, es decir, la recuperación/almacenamiento son eficientes).

Como ya se mencionó, todos los elementos de un arreglo sólo deben poseer un tipo de dato en particular (tipo de dato por valor o tipo de dato por referencia), es decir, si en la declaración especificamos que los elementos de la variable son objetos string, no podremos localizar o asignar objetos que no mantengan ninguna relación con este tipo de dato.

En puntos posteriores veremos los diferentes tipos de arreglos que soporta el lenguaje de programación C#, de acuerdo al número de dimensiones que poseen: de una sola dimensión, de dos dimensiones (matrices o tablas), multidimensionales (o también conocidos como arreglos rectangulares), y además, los arreglos de arreglos (dentados, indentados). Éstos posibilitan el diseño de algoritmos que resuelven diferentes problemas de almacenamiento y recuperación de datos en aplicaciones.

Otra característica fundamental de está estructura de dato, es que el primer elemento del arreglo empieza por el índice (locación o referencia) cero. También, vale nombrar (de acuerdo a [2]), que a diferencia de otros lenguajes de programación, en particular Java, que permite cierta flexibilidad en la sintaxis de declaración de un arreglo: los paréntesis rectangulares pueden ir frente al tipo de dato, o frente al identificador; en C# sólo está permitido colocar los paréntesis frente al tipo de dato:

int[] arregloEnteros; // esto no está permitido: int arregloEnteros[]

2. ¿Cómo Declarar un Arreglo? (Básico)

En C#, los arreglos se denotan a través de los paréntesis rectangulares ([, y ]) después del tipo de dato del arreglo. Para particularizar, veamos el siguiente ejemplo:

char[] vocales = new char[5];
covales[0] = 'a';
covales[1] = 'e';
covales[2] = 'i';
covales[3] = 'o';
covales[4] = 'u';
Console.WriteLine (covales[1]); // e

Lo que básicamente he hecho consiste en declarar un arreglo de elementos tipo char con longitud de 5. A continuación, en las posiciones disponibles del arreglo (0-4) se asignan datos de tipo char ('a''e''i''a', y 'a'). Finalmente, se muestra en pantalla (a través de la salida estándar) el valor del elemento ubicado en la posición 1.

[Nota: Más adelante veremos estructuras de repetición que permiten recorrer el listado completo elementos en un arreglo, por ejemplo, el ciclo for.] 

for (int i = 0; i < vocales.Length; i++)
Console.Write(vocales[i]); // aeiou

3. Declaración de Arreglos

Arriba hablamos de los diferentes tipos de arreglos que soporta C#, ahora les presentaré cómo se declaran utilizando la sintaxis correspondiente que ofrece el lenguaje como tal:

Arreglos de una Dimensión

int[] numeros;

Arreglos Multidimensionales

string[,] nombres;

Arreglos de Arreglos (o Dentados)

byte[][] puntajes;

4. Inicialización de Arreglos

Además de declarar el tipo de dato del arreglo, y su identifcador, es neceario que los creemos (es decir, que especifiquemos la sintaxis neceario para localizar memoria). Debido a la naturaleza de los arreglos, éstos son considerados objetos, y por lo tanto deben ser instanciados antes de ser usados. En el código fuente C# que viene a continuación se presenta cómo crear arreglos:

Arreglos de una Dimensión

int[] numeros = new int[5];

En este código utilizamos la palabra clave new y dentro de los paréntesis rectangulares después del tipo de dato (int), especificamos el número de elementos que contendrá el arreglo.

Arreglos Multidimensionales

string[,] nombres = new string[5,4];

Se trata de la inicialización de un arreglo bidimensional que tiene 5 filas y 4 columnas.

Arreglo de Arreglos (o Dentados)

byte[][] puntajes = new byte[5][];

En la creación de este arreglo de arreglos hemos creado 5 arreglos que cada uno puede contar n cantidad de arreglos. Veamos cómo créarlos:

for (int x = 0; x < puntajes.Length; x++)
{
puntajes[x] = new byte[4];
}

La instrucción dentro del ciclo for asigna un arreglo con cuatro elementos (new byte[4]).

Si queremos declarar un arreglo de más de dos dimensiones, podemos utilizar la siguiente sintaxis para crear uno de 4 filas, 5 columnas, y con 3 unidades de profundidad (como si tratara de un rectángulo en 3 dimensiones):

int[,,] botones = new int[4,5,3];

Gracias a la flexibilidad del languaje C#, podemos crear combinaciones de estos tres tipos de arreglos para declarar, por ejemplo, un arreglo de una dimensión con uno de tres dimensiones, y otro de dos dimensiones:

int[][,,][,] numeros;

Ejemplo

Archivo C# DeclaracionArreglos.cs [enlace alternativo]:

4. Inicialización de Arreglos

La inicialización de arreglos ocurre cuando enlistamos los valores de los elementos para el arreglo dentro de llaves ({, y }). Es necesario anotar que la declaración de arreglos inicializa los valores de sus elementos con el valor por defecto del tipo de dato del arreglo (para el caso de los tipos de datos numéricos) y en null para los tipos de datos por referencia.

Arreglos de una Dimensión

La sintaxis para inicializar un arreglo de una sola dimensión es muy sencilla y fácil. Es como sigue:

int[] numeros = new int[5] {1, 2, 3, 4, 5};
string[] nombres = new string[3] {"Juan", "Oliva", "Valentina"};

Gracias a que C# es un lenguaje flexible, nos permite inicializar un arreglo sin necesidad de especificar el número de elementos dentro de los paréntesis rectangulares:

int[] numeros = new int[] {1, 2, 3, 4, 5};
string[] nombres = new string[] {"Juan", "Oliva", "Valentina"};

Además, también está permitido omitir el operador new para la creación o instanciación del arreglo:

int[] numeros = {1, 2, 3, 4, 5};
string[] nombres = string[3] {"Juan", "Oliva", "Valentina"};

Arreglos Multidimensionales

Paso a explicar cómo se inicializa los arreglos multidimensionales. Al igual que en los uni-dimensaionales, la inicialización es un proceso sencillo y fácil:

int[,] numeros = new int[3, 2] { {1, 2}, {3, 4}, {5, 6} };
string[,] nombres = new string[2, 2] { {"Oliva", "Juan"}, {"Helen", "John"} };

Sin especificar el número de elementos (filas y columnas):

int[,] numeros = new int[,] { {1, 2}, {3, 4}, {5, 6} };
string[,] nombres = new string[,] { {"Oliva", "Juan"}, {"Helen", "John"} };

Sin usar el operador new:

int[,] numeros = { {1, 2}, {3, 4}, {5, 6} };
string[,] nombres = { {"Oliva", "Juan"}, {"Helen", "John"} };

Arreglo de Arreglos (Arreglos Dentados)

int[][] numeros = new int[2][] { new int[] {2,3,4}, new int[] {5, 6, 7, 8, 9} };

Sin especificiar el número de arreglos:

int[][] numeros = new int[][] { new int[] {2,3,4}, new int[] {5, 6, 7, 8, 9} };

U omitiendo el operador de ínstanciación new:

int[][] numeros = { new int[] {2,3,4}, new int[] {5, 6, 7, 8, 9} };

5. Acceso a los Miembros de un Arreglo

Cuando ya hayamos declarado un arreglo, ipso facto, podemos acceder/manipular sus elementos, ya sea para lectura y/escritura. Al igual que muchos otros lenguajes, C# hereda la sintaxis de los lenguajes clásicos C y C++.

Arreglos de una Dimensión

En el código fuente de ejemplo que presento ahora, una vez declare e inicialice el arreglo numeros, puedo modificar o alterar cualquier de los elementos. Miremos:

int[] numeros = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
numeros[4] = 5;

La segunda línea de código fuente C#, permite acceder a la ubicación o índice número 4 y asignar el valor 5 al elemento en esa ubicación.

Arreglos Multidimensionales

De forma muy similar, en los arreglo multidimensionales, el acceso a elementos se lleva a cabo de este modo: 

int[,] numeros = { {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10} };
numeros[1, 1] = 5;

Aquí, se asigna 5 al segundo elemento de la segunda fila.

Arreglo de Arreglos (o Arreglos Dentados)

El código fuente:

int[][] numeros = new int [][] { new int[] {1, 2}, new int[] {3,4,5} };

Crea un arreglo de elementos de tipo de dato entero de una dimensión que contiene dos elementos: un arreglo de enteros con dos elementos, y otro arreglo con 3 elementos).

En la siguiente sintaxis modificamos el primer arreglo sobre la primera posición:

numeros[0][0] = 26;

En este otro, asignamos 29 al segundo elemento del segundo arreglo:

numeros[1][1] = 29;

6. Todos los Arreglos son Objetos

En la especificación de C#, está claro que todos los arreglos son considerados objetos. Todos los tipos de arreglos derivan de la clase superior (o padre) System.Array [3]. Esta característica, nos permite realizar acciones (llamadas a funciones) y por ende diseñar algoritmos siguiendo el paradigmo orientado a objetos.

Para demostrarlo, en este ejemplo presento el uso de la propiedad Length [4]:

int[] numeros = {1, 2, 3, 4, 5};
int cantidadElementos = numeros.Length;

En la documentación de la clase Array [3] encontraremos otros métodos y propiedades interesantes y útiles para el desarrollo de algoritmos.

7. Uso de la Construcción foreach en Arreglos

Si queremos recorrer o iterar todo el conjunto de elementos de un arreglo, C# provee una forma fácil, simple y cómoda de llevarlo a cabo a través de la construcción iterativa foreach.

En el siguiente código declaramos e inicializamos un arreglo con 9 elementos de tipo entero, enseguida, recorremos ese arreglo con la construcción mecionada; así:

int[] numeros = {4, 5, 6, 1, 2, 3, -2, -1, 0};
foreach (int i in numeros)
{
System.Console.WriteLine (i);
}

Esto mismo podemos hacer con un arreglo multidimensional (tabla, o matriz):

int[,] numeros = new int[3, 2] {{9, 99}, {3, 33}, {5, 55}};
foreach (int i in numeros)
{
Console.Write ("{0} ", i);
}

Más adelante presentaré la construcción repetitiva for, la cual nos da más poder y control para manipular los elementos de un arreglo.

8. Chequeo de Límites de Arreglos

Una de las tareas de la CLR de C# es comprobar en tiempo de ejecución que los límites de un arreglo no sean excedidos. Cuando esto ocurre la CLR lanza la excepción IndexOutOfRangeException [5]. Por ejemplo:

int[] nuevoArreglo = new int[3];
nuevoArreglo[3] = 1; // se lanza la excepción IndexOutOfRangeException

En [1], así como en Java, bajo C# el chequeo de límites es necesario para la seguridad de tipos y la simplificación del proceso de depuración.

[Nota: De acuerdo con [1], el impacto en el rendimiento sobre el chequeo de límites de arreglos no es significativa. Sin embargo, el compilador JIT (Just-in-Time) lleva a cabo optimizaciones como la de determinar si en un ciclo se lleguen a exceder los límites del arreglo, en lugar de hacer esto por cada iteración.]

[Nota: En el lenguaje C#, es posible marcar código como inseguro que omita el chequeo de límites en tiempo de ejecución por parte de la CLR.]

9. Conclusiones

En este artículo he presentando una de las construcciones o estructuras de datos más interesante y básica para la manipulación de datos de programa: los arreglos. Se ha visto la sintaxis de declaración, inicialización o instanciación, y acceso a los elementos de un arreglo. También presenté la relación que tienen los arreglos con el paradigma orientado a objetos: todos los arreglos son objetos. Su estrecha relación con la clase System.Array, el uso particular de la propiedad Length. Al final mostré la construcción iterativa o repetitiva foreach que permite recorrer o iterar a través de todos los elementos de un arreglo.

10. Glosario

- Arreglo dentado
- Depuración
- Lenguaje C
- Lenguaje C++
- Orientación a Objetos

11. Referencias

[1] C# 5.0 in a Nutshell by Joseph Albahari and Ben Albahari. Copyright 2012 Joseph Albahari and Ben Albahari, 978-1-449-32010-2.
[2] Arrays Tutorial (C#) - http://msdn.microsoft.com/en-us/library/aa288453(v=vs.71).aspx
[3] Array Class (System) - http://msdn.microsoft.com/en-us/library/system.array.aspx
[4] Array.Length Property (System) - http://msdn.microsoft.com/en-us/library/system.array.length.aspx
[5] IndexOutOfRangeException Class (System) - http://msdn.microsoft.com/en-us/library/system.indexoutofrangeexception.aspx


H

5 comentarios:

  1. Buenas tardes.

    En el primer código de ejemplo pone:

    covales[0] = 'a';
    covales[0] = 'e';
    covales[0] = 'i';
    covales[0] = 'o';
    covales[0] = 'u';

    Debería ser:

    covales[0] = 'a';
    covales[1] = 'e';
    covales[2] = 'i';
    covales[3] = 'o';
    covales[4] = 'u';

    Un saludo y gracias.

    ResponderEliminar
    Respuestas
    1. Actualizado.

      De nuevo, gracias por el reporte de erratas.

      Saludos desde Bogotá D.C.

      Eliminar
  2. En el punto 5, en la parte de "Arreglo de Arreglos (o Arreglos Dentados)", el siguiente código está mal:

    int[][] numeros = int [][] { new int[] {1, 2}, new int[] {3,4,5} };

    O le falta el new de la asignación (¿se dice así?), o sobre el "int [][]"

    ResponderEliminar
    Respuestas
    1. Corregido.

      El operador `new` se usa para asignar memoria de trabajo a una estructura de datos; en este caso a un arreglo bidimensional.

      Gracias.

      Saludos.

      Eliminar
  3. como hago para que un arreglo no empiece a desde 0

    ResponderEliminar

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.