La siguiente entrada trata sobre las listas multidimensionales en Java, un concepto que no existe pero cuyo propósito es perfectamente alcanzable utilizando la programación adecuada. Esto intenta mostrarle al lector que las Listas son las mejores opciones disponibles en Java frente a las clásicas matrices y arreglos.
En el día de ayer publiqué un post sobre las listas en Java. En dicho post hablo un poco acerca de los aspectos básicos relacionados a el uso de las listas. Me he referido a este tipo de variables como una opción ante los arreglos y las matrices. Sin embargo en ningún momento mencioné nada sobre el manejo de datos en dos o más dimensiones. Este post lo dedicaré a explicar cómo podemos guardar datos en listas con más de una dimensión.
De hecho, no existe tal cosa como listas multidimensionales. Ese concepto no aplica para este tipo de variables. Entonces, ¿cómo por qué he titulado esta entrada como «Listas multidimensionales en Java»? Pues es simple, podemos crear listas dentro de listas trabajar con instancias de una clase diseñada específicamente para este propósito. Veamos cómo es esto.
Listas dentro de listas
Para los ejemplos a lo largo de esta entrada utilizaré la variable ejemploLista. Cuando declaramos una lista utilizamos la siguiente sintaxis.
1 |
List<objeto> ejemploLista = new ArrayList<String>(); |
En objeto colocamos el tipo de variable que queremos almacenar en la lista. Pero bien podemos lograr que el objeto que usamos como argumento sea otra lista. Esto se daría de la siguiente forma:
1 |
List<List<String>> ejemploLista = new ArrayList<List<String>>(); |
Entonces, al colocar una lista dentro de otra podemos guardar información en filas y columnas. Nuestras columnas serían listas y nuestras filas los datos en el interior de dichas listas.
Esta es una visualización gráfica de listas multidimensionales.
Para agregas las sub listas a nuestra variable ejemploLista utilizamos la siguiente instrucción:
1 |
ejemploLista.add(new ArrayList<String>()); |
Hemos agregado una sub lista al índice 1 de nuestra variable ejemploLista. Ahora si queremos agregar datos a dicha lista pues la llamamos y la modificamos de la siguiente manera:
1 |
ejemploLista.get(0).add("Valor que deseamos insertar"); |
Al utilizar el método get() estamos llamando a una lista. Sobre dicha lista utilizamos el método add() e insertamos el valor deseado.
Yo soy de los que cree que con el ejemplo es que se aprende, así que vamos a llevar a cabo una pequeña tarea. Crearemos 2 listas, una para la empresa A y otra para la empresa B. En cada lista vamos a insertar los nombres, apellidos, edades e identificaciones de los 3 trabajadores de cada empresa. Estos són:
Entonces vamos a declarar 2 listas, llamadas listaEmpresaA y listaEmpresaB.
1 2 |
List<List<String>> listaEmpresaA = new ArrayList<List<String>>(); List<List<String>> listaEmpresaB = new ArrayList<List<String>>(); |
Ahora en cada una de las dos listas tenemos que agregar 5 sublistas, lo cual representará nuestras 5 columnas.
1 2 3 4 |
for(int i =0; i<= 4; i++){ listaEmpresaA.add(new ArrayList<String>()); listaEmpresaB.add(new ArrayList<String>()); } |
Las 5 columnas las guardaré como String. Bien podríamos guardar las edades como un entero (int) pero esto es sólo un ejemplo y no lo voy a tomar en cuenta.
Ahora voy a proceder a introducir los datos en cada una de las listas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
//Lista de la empresa A listaEmpresaA.get(0).add("8-830-545"); listaEmpresaA.get(0).add("7-346-567"); listaEmpresaA.get(0).add("8-760-455"); listaEmpresaA.get(1).add("Juan"); listaEmpresaA.get(1).add("Pedro"); listaEmpresaA.get(1).add("María"); listaEmpresaA.get(2).add("Pérez"); listaEmpresaA.get(2).add("González"); listaEmpresaA.get(2).add("Rodríguez"); listaEmpresaA.get(3).add("26"); listaEmpresaA.get(3).add("32"); listaEmpresaA.get(3).add("29"); //Lista de la empresa B listaEmpresaB.get(0).add("8-745-897"); listaEmpresaB.get(0).add("9-379-392"); listaEmpresaB.get(0).add("8-732-672"); listaEmpresaB.get(1).add("José"); listaEmpresaB.get(1).add("María"); listaEmpresaB.get(1).add("Matías"); listaEmpresaB.get(2).add("Alonso"); listaEmpresaB.get(2).add("Moreno"); listaEmpresaB.get(2).add("Ortega"); listaEmpresaB.get(3).add("31"); listaEmpresaB.get(3).add("30"); listaEmpresaB.get(3).add("32"); |
Ahora crearé una rutina para imprimir la información almacenada:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
System.out.println("Empleados de la Empresa A"); System.out.println("-------------------------"); for (int i = 0; i <= listaEmpresaA.get(0).size() - 1; i++) { System.out.println("ID: " + listaEmpresaA.get(0).get(i) + " Nombre: " + listaEmpresaA.get(1).get(i) + " " + listaEmpresaA.get(2).get(i) + " Edad: " + listaEmpresaA.get(3).get(i) ); } System.out.println("-------------------------"); System.out.println("Empleados de la Empresa B"); System.out.println("-------------------------"); for (int i = 0; i <= listaEmpresaB.get(0).size() - 1; i++) { System.out.println("ID: " + listaEmpresaB.get(0).get(i) + " Nombre: " + listaEmpresaB.get(1).get(i) + " " + listaEmpresaB.get(2).get(i) + " Edad: " + listaEmpresaB.get(3).get(i) ); } |
El resultado que obtendremos es el siguiente:
Vemos que efectivamente podemos guardar información y consultarla con este tipo de modelo. Sin embargo esta forma de guardar datos posee una ventaja fundamental y es el hecho de que aunque tenemos información guardada en varias sub listas, estas no se encuentran relacionadas entre sí, por lo que si en algún momento deseamos ordenar la información, modificarla o eliminarla tendremos que hacer lo mismo para todas y cada una de las sub listas contenidas dentro de una lista.
Este tipo de modelo funciona cuando tenemos grandes cantidades de datos y nuestras tareas se basan en hacer consultas sobre dicha información.
El siguiente modelo de almacenamiento de datos presenta una serie de ventajas sobre el modelo de las sub listas y es mi modelo preferido para el almacenamiento de información.
Objetos dentro de Listas
La que es probablemente la mejor forma de usar listas multidimensionales en Java es a través de una clase que utilizamos como objeto dentro de los parámetros de la lista.
En nuestro ejemplo vamos a crear una clase llamada empresa.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public static class empresa { private String ID; private String Nombre; private String Apellido; private int Edad; private String Empresa; private empresa(String _ID, String _Nombre, String _Apellido, int _Edad, String _Empresa) { ID = _ID; Nombre = _Nombre; Apellido = _Apellido; Edad = _Edad; Empresa = _Empresa; } } |
Esta clase la colocamos en la misma clase de nuestro ejemplo donde tenemos nuestro método main. En mi caso mi proyecto se llama PHTK_statistics.
Nótese que al declarar una clase y colocar en su interior un método con el nombre de la clase (constructor, en dicho método podemos establecer parámetros para la creación de objetos de esa clase. Al crear un objeto de esa clase se deben establecer obligatoriamente los parámetros indicados en el constructor de la clase.
Dentro de la clase empresa colocamos el constructor:
1 |
private empresa(String _ID, String _Nombre, String _Apellido, int _Edad, String _Empresa) |
Si deseamos crear un objeto de esta clase, la declaración se debe hacer de la siguiente forma:
1 |
empresa e = new empresa("8-830-545","Juan","Pérez", 26, "A"); |
El constructor empresa se invoca cuando se crea una instancia de la clase empresa. En dicho constructor hemos asignado los parámetros a unas variables globales que hemos declarado en la clase.
Ahora vamos a crear nuestra variable ejemploLista nuevamente, utilizando la clase empresa como parámetro. En nuestro método main escribimos:
1 |
List<empresa> ejemploLista = new ArrayList<empresa>(); |
Ahora vamos a proceder a insertar los mismos datos que utilizamos un poco más arriba con las sub listas.
1 2 3 4 5 6 |
ejemploLista.add(new empresa("8-830-545", "Juan", "Pérez", 26, "A")); ejemploLista.add(new empresa("7-346-567", "Pedro", "González", 32, "A")); ejemploLista.add(new empresa("8-760-455", "María", "Rodríguez", 29, "A")); ejemploLista.add(new empresa("8-745-897", "José", "Alonso", 31, "B")); ejemploLista.add(new empresa("9-379-392", "María", "Moreno", 30, "B")); ejemploLista.add(new empresa("8-732-672", "Matías", "Ortega", 32, "B")); |
De esta forma tenemos nuestros datos almacenados en objetos de la clase empresa. Podemos consultar fácilmente la información que hemos almacenado.
1 2 3 4 5 6 7 |
System.out.println("Lista de empleados por cada empresa"); System.out.println("-----------------------------------"); for (int i = 0; i <= ejemploLista.size()-1; i++) { System.out.println("ID: " + ejemploLista.get(i).ID + " Nombre: " + ejemploLista.get(i).Nombre + " " + ejemploLista.get(i).Apellido + " Edad: " + ejemploLista.get(i).Edad + " Empresa: " + ejemploLista.get(i).Empresa); } |
Esto generará el siguiente resultado:
Efectivamente tenemos nuestra información tal como la queremos. Pero, el utilizar objetos en vez de listas como parámetros de las listas (valga la redundancia) tiene sus ventajas. Por ejemplo si queremos que los empleados de cada empresa se impriman por separado utilizamos el siguiente código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
System.out.println("Lista de empleados por cada empresa"); System.out.println("------------------------------------"); System.out.println("Empresa A"); System.out.println("-----------------------------------"); for (int i = 0; i <= ejemploLista.size() - 1; i++) { if (ejemploLista.get(i).Empresa.equals("A")) { //<-- sólo se toman encuenta los empleados de la empresa A System.out.println("ID: " + ejemploLista.get(i).ID + " Nombre: " + ejemploLista.get(i).Nombre + " " + ejemploLista.get(i).Apellido + " Edad: " + ejemploLista.get(i).Edad + " Empresa: " + ejemploLista.get(i).Empresa); } } System.out.println("------------------------------------"); System.out.println("Empresa B"); System.out.println("-----------------------------------"); for (int i = 0; i <= ejemploLista.size() - 1; i++) { if (ejemploLista.get(i).Empresa.equals("B")) { //<-- sólo se toman encuenta los empleados de la empresa B System.out.println("ID: " + ejemploLista.get(i).ID + " Nombre: " + ejemploLista.get(i).Nombre + " " + ejemploLista.get(i).Apellido + " Edad: " + ejemploLista.get(i).Edad + " Empresa: " + ejemploLista.get(i).Empresa); } } |
Vemos que con la condición:
1 |
ejemploLista.get(i).Empresa.equals("A") |
Solo se aceptan los elementos de la empresa A. Y esta es una ventaja de las listas con objetos ya que están relacionados unos con otros y podemos por ejemplo ordenar datos en base a la edad, al nombre, apellido, ID o cualquier otro parámetro que deseemos. Esta línea arroja el siguiente resultado:
Cuando decidamos eliminar un registro podemos eliminar una fila completa sin tener que eliminar cada columna de cada fila como tendríamos que hacer al usar listas dentro de listas.
Por ejemplo para eliminar los datos relacionados a José Alonso, eliminamos el registro 4 de la siguiente forma:
1 |
ejemploLista.remove(3); |
Es mucho más sencillo que tener que eliminar columna por columna. De hecho este post es un preámbulo de otro que haré sobre las listas y el uso de Lambda Expresions de Java 8, donde el declarar listas como objetos como parámetros es elemental para lograr una mejor eficiencia en nuestro código y aprovechar el poder de las expresiones Lambda.
Sobre el código mostrado en este ejemplo me queda decir pues que a pesar que cuando introducimos la información modificamos directamente las variables declaradas dentro de la clase y cuando hacemos las consultas llamamos a estas variables de forma directa, esta no es la forma «educada» de hacer las cosas en Java. De hecho este método funciona, no hay ningún problema en ello, pero la forma correcta de hacerlo es a través de métodos que declaramos dentro de la clase para la modificación de las variables y las consultas sobre las mismas.
Modificamos nuestra clase empresa de la siguiente manera:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
public static class empresa { private String ID; private String Nombre; private String Apellido; private int Edad; private String Empresa; private empresa(String _ID, String _Nombre, String _Apellido, int _Edad, String _Empresa) { setID(_ID); setName(_Nombre); setLastName(_Apellido); setAge(_Edad); setBusiness(_Empresa); } public String getID() { return ID; } public String getName() { return Nombre; } public String getLastName() { return Apellido; } public int getAge() { return Edad; } public String getBuiness() { return Empresa; } public void setID(String _ID) { ID = _ID; } public void setName(String _Nombre) { Nombre = _Nombre; } public void setLastName(String _Apellido) { Apellido = _Apellido; } public void setAge(int _Edad) { Edad = _Edad; } public void setBusiness(String _Empresa) { Empresa = _Empresa; } } |
La declaración de las listas y la inserción de datos permanecen invariables. Las consultas varían ligeramente:
1 2 3 |
System.out.println("ID: " + ejemploLista.get(0).getID() + " Nombre: " + ejemploLista.get(0).getName()+ " " + ejemploLista.get(0).getLastName() + " Edad: " + ejemploLista.get(0).getAge()+ " Empresa: " + ejemploLista.get(0).getID()); |
Si deseamos modificar cierta información entonces usamos los métodos set(setName(), setLastName…).
Repito, manejar las variables contenidas en la clase empresa de forma directa es perfectamente posible y funcional, pero se prefiere los manejos de variables a través de métodos.
Esto es todo pro ahora. Mi próximo post será sobre manejos de grandes volúmenes de datos contenidos en listas utilizando el poder de las Expresiones Lambda en Java 8.
Espero que la información suministrada se de utilidad para usted. Saludos.