Arreglos bidimensionales en Java
Continuando con las estructuras de datos simples, en esta ocasión toca el turno a los arreglos bidimensionales.
La dimensión.
La dimensión de un arreglo es el tamaño del arreglo, y define la forma de organizar los datos. Un arreglo puede ser unidimensional (tener sólo una dimensión), bidimensional (dos dimensiones) y tridimensional (tres dimensiones). Para más sobre los arreglos unidimensionales, puedes leer aquí.
La organización lógica de los datos en un arreglo nos permite utilizarlos de forma muy diversa. En realidad, Java no pone restricciones en cuanto al número de dimensiones que puede tener un arreglo, pero para los programadores es más compleja su manipulación a mayor número de dimensiones.
En la figura 1 te muestro los dos arreglos multidimensionales más populares: los bidimensionales, también conocidos como matrices, y los tridimensionales que representan lo que podríamos imaginarnos como un cubo de datos.

Figura 1. Ejemplos de arreglos multidimensionales
Arreglos bidimensionales
Es muy común llamarles arreglos bidimensionales, arreglos 2d, o matrices, pero en realidad, se tratan de un arreglo de arreglos. Sólo que para su manipulación lógica, ha sido más conveniente pensar en ellos como una tabla de valores. (De ahí que decidí hacer uso, una vez más, del trabajo fotográfico de Carlos ZGZ para comparar la organización de un edificio de departamentos con los datos almacenados en un arreglo bidimensional).
Declaración y construcción
La declaración de un arreglo bidimensional en Java se muestra a continuación:

Figura 2. Declaración de un arreglo bidimensional
Como puedes observar ahora tenemos dos dimensiones: el número de renglones y el número de filas del arreglo.
Al igual que los arreglos unidimensionales, el proceso de declaración y construcción puede realizarse en una sola sentencia o en dos. En la figura 3 puedes ver la instrucción que crea una matriz.

Figura 3. Construcción de una matriz o arreglo bidimensional.
Al igual que los arreglos unidimensionales, las dimensiones de una matriz deben:
- Ser un valor entero.
- Pueden expresarse como constantes numéricas (10, 100, 1000, etc)
arreglo = new int[10][5];
- También pueden expresarse como una constante simbólica, ya sea las dos dimensiones o sólo una de ellas.
public static final R = 10; public static final C = 6; . . . matriz = new boolean[R][C];
- Y también pueden usarse variables enteras previamente inicializadas.
int renglones = 12; int columnas = 8; matriz = new double[renglones][columnas];
- La primera dimensión se refiere al total de renglones de la matriz, y la segunda se refiere al total de columnas.
La propiedad length.
Como ya se mencionó en la entrada sobre arreglos unidimensionales, todo arreglo declarado y construido apropiadamente es de hecho un objeto especial. Los objetos en Java tienen métodos y propiedades.
En el caso de un arreglo de cualquier tipo de elementos, su única propiedad es length, la cual contiene el número de elementos en el arreglo. Pero para un arreglo bidimensional, esta propiedad puede indicar tanto el número de renglones como el número de columnas, dependiendo de cómo se utilice. Observa la figura 4:

Figura 4. Uso de la propiedad length en un arrego bidimensional.
En la línea 1 se declara una matriz de enteros de 5 renglones por 3 columnas. Luego se declara la variable x, y se le almacena el resultado de la propiedad length. En este punto, x = 5, es decir, el número de renglones.
Después en la línea 3 se vuelve a utilizar la propiedad length, pero ahora se establece el número de renglón en 2. En este caso, se obtiene el total de columnas que tiene la matriz en el renglón 2.
¿Esto quiere decir que cada renglón puede alojar un número diferente de renglones? Continúa leyendo, la cosa se pone interesante.
Tipos de matrices.
Como puedes deducir del punto anterior, existen diversos tipos de matrices:
Matrices cuadradas
Una matriz cuadrada es aquella que tiene el mismo número de renglones como de columnas. Son las matrices que más se utilizan en el álgebra, pues tienen ciertas propiedades que les permiten ser usadas en operaciones como la suma de matrices. En la figura 5 puedes observar diferentes ejemplos de matrices cuadradas.

Figura 5. Ejemplos de matrices cuadradas
Por supuesto, en este tipo de casos, matriz.lenght y matriz[i].lenght generan el mismo resultado, por lo que se puede prescindir de utilizar la segunda expresión.
Matrices no cuadradas
Otro tipo de matrices son las no cuadradas, y como puedes deducir, son aquellas en donde el número de renglones no son iguales al número de columnas. Puede darse el caso que tenga más renglones que columnas o al revés. En la figura 6 puedes ver ejemplos de este tipo de matrices.

Figura 6. Ejemplos de matrices no cuadradas
Matrices irregulares.
Por otro lado, las matrices irregulares son aquellas donde uno o más renglones tienen un número variable de columnas. En la figura 7 puedes ver un ejemplo de este tipo de matrices.

Figura 7. Ejemplo de una matriz irregular.
Para la declaración y construcción de la matriz de la figura 7 se realiza lo siguiente:
int[][] matriz = new int[4][]; // Sólo se indica el número de renglones, pero no el de columnas, porque será diferente en cada renglón. matriz[0] = new int[2]; // En el renglón 0, se construyen 2 columnas matriz[1] = new int[3]; // El renglón 1 contiene 3 columnas matriz[2] = new int[2]; // El renglón 2 contiene 2 columnas matriz[3] = new int[4]; // El renglón 3 contiene 4 columnas.
Además, puedes declarar, construir e inicializar esta matriz en tiempo de edición realizando lo siguiente:
int[][] matriz = { {1, 2}, {3, 4, 5}, {6, 7}, { 8, 9, 10, 11} };
Acceso a los elementos de un arreglo.
Al igual que los arreglos unidimensionales o vectores, para acceder a los elementos de una matriz se requiere el uso de índices. Sólo que, en esta ocasión, se deberán usar dos índices, el primero de ellos para indicar el renglón y el segundo de ellos indicará la columna. (Casi como en un plano cartesiano, donde x correspondería con el número de columna, y el valor y con el número de renglón).
Una vez más, al igual que en el arreglo unidimensional, el conteo de los índices inicia en cero, por lo que si un arreglo ha sido declarado y construido de la siguiente forma:
int[][] arreglo = new int[5][4];
Tiene 20 casilas, 20 elementos, 20 espacios para almacenar datos tipo int. Cuenta con 5 renglones o filas, pero las direcciones de renglones van desde la 0 hasta la 4. También tiene 4 columnas, pero las direcciones de columnas van desde la 0 hasta la 3. Intentar acceder a la dirección de renglón 5, columna 4, generará el error que se muestra en la figura 8.

Figura 8. Excepción generada al intentar acceder a una dirección fuera de los límites
Recuerda que al trabajar con arreglos bidimensionales SIEMPRE debes especificar los dos índices: la dirección correspondiente al renglón y la dirección de la columna. En la figura 9 puedes apreciar los errores que se generan al olvidar los dos índices o incluso uno de ellos.

Figura 9. Error al olvidar escribir un índice en un arreglo bidimensional.
Introducción de datos a un arreglo bidimensional.
En la figura 10 te muestro la forma general para introducir un dato a una casilla específica de un arreglo.

Figura 10. Forma general para introducir un dato en una casilla de una matriz.
Pueden existir variaciones a esta forma general, como:
- El índice puede ser una constante numérica:
arreglo[3][2] = 9;
- El índice puede ser una variable tipo int:
arreglo[i][j] = 9; // donde i y j son variables enteras e inicializadas
- El valor puede ser un valor que cumpla con el tipo del arreglo:
arreglo[2][3] = true // si arreglo es de tipo boolean
- El valor puede ser una variable igual al tipo del arreglo:
arreglo[3][2] = x;
- El valor puede ser una operación cuyo resultado sea compatible con el tipo del arreglo:
arreglo[4][2] = x%y; //arreglo debe ser tipo int para poder almacenar este resultado
- El índice puede ser el resultado de una operación entera:
arreglo[i*2][j-2] = 12;
Agilizando la manipulación de los arreglos
Cuando se requiere realizar una misma tarea sobre cada uno de los elementos del arreglo, lo más recomendable es utilizar un ciclo para recorrer cada una de sus casillas. En el caso de las matrices, dado que tienen dos dimensiones, se debe utilizar dos ciclos for, uno anidado dentro del otro.
El ciclo más interno será el que más pronto se termine, en cambio el contador del ciclo más externo sólo cambiará su valor cada vez que el ciclo interno termine. Yo comparo los ciclos anidados con el funcionamiento de las manecillas de un reloj analógico: la manecilla de minutos sólo puede avanzar cuando el segundero termine de hacer su recorrido de 60. El segundero (que lo comparo con el ciclo más interno) avanza más rápido, en tanto que el minutero es dependiente del segundero y por lo tanto su avance es más lento.
Leyendo datos por renglones
En el siguiente ejemplo, los datos serán almacenados por renglones, eso quiere decir que recorreremos cada una de las columnas de un renglón antes de avanzar al siguiente. Esta forma es la más común para insertar datos en una matriz. (El fragmento supone que se trabaja con una matriz cuadrada).
for (int i=0; i<auxiliar.length; i++){ for (int j=0; j<auxiliar.length; j++){ System.out.println("Escribe el elemento:["+i+", "+j+"] del arreglo:"); entrada = bufer.readLine(); arreglo[i][j] = Integer.parseInt(entrada); } }
Como puedes apreciar en la animación de la figura 11, el índice de cada columna de la matriz, representado por j, es el que se mueve más rápido, mientras el índice i que representa los renglones se mantiene estático hasta que el recorrido por todas las columnas termine.

Figura 11. Animación del recorrido por renglones
Leyendo datos por columnas
Dependiendo de la organización de los datos, puedes necesitar leer los datos por columnas y no por renglones. En este caso, el siguiente fragmento de código realiza este trabajo:
for (int i=0; i<auxiliar.length; i++){ for (int j=0; j<auxiliar.length; j++){ System.out.println("Escribe el elemento:["+i+", "+j+"] del arreglo: "); entrada = bufer.readLine(); arreglo[j][i] = Integer.parseInt(entrada); } }
En esta ocasión, como puedes apreciar en la figura 12, el índice j se mantiene estático (se mueve más lento) y espera a que el índice i haga el recorrido por todos los renglones de los que se compone la matriz.
Comparte el conocimiento
Hasta aquí termina mi contribución sobre los arreglos bidimensionales o matrices.
Si te parece que este artículo te ha sido útil, recuerda compartirlo con tus amigos o con quien más lo necesite. Y no olvides escribir en los comentarios tus preguntas y/o sugerencias.
Saludos, y ¡hasta la próxima entrada!
Alan Juarez Fragoso 4A DSM
Daniel Sammuel Cuayahuitl Pérez 4°A DSM
Juan Daniel González Vásquez 4 A DSM
william Aguilar Chacon
Leído Profesora
Daniel Fernandez Guarneros 4A DSM
Jose Gustavo Barrera Valerio 4A DSM