INACAP Universidad Tecnológica de Chile Sede Santiago Centro Taller de Programación I Curso Java J2SE Tema 08: Estructuras Dinámicas de Datos en Java Ing. Manuel López Ramos
Parte I Qué son las estructuras de datos?
Si en un programa de computador se hace necesario trabajar con una gran cantidad de datos, o bien, no se conoce inicialmente la cantidad máxima de datos a almacenar/procesar en la aplicación, entonces se necesitará contar con una estructura de datos que pueda crecer indefinidamente en el tiempo. Por esta razón, en Computación se reconocen 2 tipos de estructuras de datos: estructuras de datos estáticas estructuras de datos dinámicas
Por esta razón, en Computación se reconocen 2 tipos de estructuras de datos: * Variables * Arreglos * Matrices Su tamaño NO puede cambiar luego de haberse definido en el programa
Por esta razón, en Computación se reconocen 2 tipos de estructuras de datos: * Variables * Arreglos * Matrices * Listas * Pilas * Colas * Árboles * Diccionarios * Grafos Su tamaño SÍ puede cambiar luego de haberse definido en el programa
En Java, existen diversas clases para implementar estas estructuras dinámicas. Estas son las más generales: Listas: List, ArrayList, LinkedList Pilas: Stack Colas: Queue Árboles: TreeMap Diccionarios: HashMap Grafos: HashMap De estas estructuras de datos, la más simple (y útil para el curso) es la de lista, que será implementada en los programas mediante la clase de Java ArrayList.
Parte II Estructuras de datos dinámicas básicas en Java
La clase de Java ArrayList representa las estructuras de datos denominadas como listas simples enlazadas. Estas estructuras pueden crecer indefinidamente debido a que implementan una estructura base de almacenamiento de datos, llamada NODO DE DATOS. El NODO DE DATOS puede almacenar un dato de cualquier tipo y posee una referencia (o enlace o apuntador) que le permite conectarse hacia otro nodo más adelante. Se simboliza como se muestra a continuación: Referencias Lista simple Nodos de datos
Se muestra a continuación la forma de creación y uso de un ArrayList en Java: ArrayList lista = new ArrayList(); (crea una lista vacía, con el nombre dado) lista.add(-1); lista.add(45.0); lista.add("sí"); lista.add('@'); 1) Debe crearse una lista vacía, con un nombre de variable. 2) Los datos se van insertando mediante el método add.
Todos los elementos de la lista están ubicados en una posición (como en los arreglos), donde el primer elemento estará en la posición 0. Para recuperar un elemento de la lista, se utiliza el método get(posición). lista.get(0); (devuelve el dato -1) lista.get(1); (devuelve el dato 45.0) lista.get(3); (devuelve el dato '@') Si se desea conocer el largo de la lista (es decir, la cantidad de elementos que posee), se debe utilizar el método size() sobre la lista. lista.size()
En algunos casos, el método get no devuelve el dato con el tipo de datos que el programa esperaría. En esos casos, se hace obligatorio utilizar casting de datos (conversión de tipo) para obtener el dato correcto. (int)lista.get(0); (devuelve el dato -1 como int) (double)lista.get(1); (devuelve el dato 45.0 como double) (String)lista.get(2); (devuelve el dato 45.0 como String) (char)lista.get(3); (devuelve el dato '@' como char)
Finalmente, el método remove(posición) elimina un nodo de la lista, según la posición dada. lista.remove(3); (se elimina el dato '@') lista.remove(1); (se elimina el dato 45.0') lista.remove(0); (se elimina el dato -1) lista.remove(0); (se elimina el dato "Sí") (la lista queda vacía) Nótese que al eliminar elementos intermedios, los elementos de la derecha se mueven hacia atrás para rellenar el espacio vacío. Así también, los elementos de la derecha cambiarán sus posiciones al moverse hacia atrás.
Parte III Implementación de cardinalidades para relaciones de asociación entre clases
Cuando la cardinalidad de una relación es baja (del orden de 1, 2 ó 3), en general bastará con crear objetos, dentro de variables simples, en la cantidad indicada. Pero si la cardinalidad es grande, conviene agrupar los objetos en estructuras de datos que se puedan acceder y recorrer con facilidad; esto es, en arreglos o listas. Vea los siguientes ejemplos: a) Una mesa tiene 4 patas public class Mesa private Pata pata1, pata2, pata3, pata4; public Mesa() this.pata1 = new Pata(); this.pata2 = new Pata(); this.pata3 = new Pata(); this.pata4 = new Pata(); public class Pata
b) Un bus tiene entre 0 y 40 pasajeros public class Bus private Pasajero[] arrpasajeros; public Bus() // Sólo basta con crear el arreglo // vacío, ya que la cardinalidad 0 // está aceptada en la relación. this.arrpasajeros = new Pasajero[40]; public class Pasajero
c) Un partido de fútbol se juega con 22 jugadores public class PartidoFutbol private Jugador[] arrjugadores; public PartidoFutbol() // Aparte de crear el arreglo, debe // crear cada uno de los 22 jugadores // que indica la cardinalidad. this.arrjugadores = new Jugador[22]; for(int pos=0; pos<22; pos++) this.arrjugadores[pos] = new Jugador(); public class Jugador
d) Un paciente puede registrar 0 ó más enfermedades import java.util.arraylist; public class Paciente private ArrayList<Enfermedad> listaenf; public Paciente() // Al crear la lista, esta tendrá 0 objetos this.listaenf = new ArrayList<Enfermedad>(); public class Enfermedad
Según lo visto en las diapositivas anteriores, es muy importante cumplir con las relaciones y con la cardinalidad, debido a que el diseño de clases determina la cantidad de objetos participantes de las relaciones y a que, si no se crea la cantidad indicada, el programa no contará con los objetos que necesita por especificación del problema. Ahora bien, conviene también entender cómo lograr acceder a los objetos que se almacenan en cada estructura de datos. Para esto, véase el siguiente diagrama de UML, donde se relacionan tres clases entre sí con cardinalidades grandes: e) Una clínica puede tener 0 ó más pacientes, mientras que cada paciente puede tener entre 0 y 20 enfermedades registradas.
De acuerdo a lo indicado en las diapositivas anteriores: * La relación Clinica-Paciente se implementará con un ArrayList (por la cardinalidad sin valor máximo). * La relación Paciente-Enfermedad se implementará mediante un arreglo de objetos (por su cardinalidad con valor máximo 20).
El código que implementará éstas relaciones es el mostrado a continuación (parte 1): public class Paciente private String nombre; private byte edad; private Enfermedad[] arrenf; public Paciente() // Al crear el arreglo, tendrá 0 objetos. this.arrenf = new Enfermedad[20]; public class Enfermedad private String sintomas; public String getsintomas() return this.sintomas; public void setsintomas(string v) this.sintomas = v;
El código que implementará éstas relaciones es el mostrado a continuación (parte 2): public class Clinica private String nombre; private ArrayList<Paciente> listapac; public Clinica() // Esta lista se crea sin elementos // inicialmente. this.listapac = new ArrayList<Paciente>(); public class Paciente private String nombre; private byte edad; private Enfermedad[] arrenf; public Paciente() // Al crear el arreglo, tendrá 0 objetos. this.arrenf = new Enfermedad[20];
Nótese también que, al crear nuevos atributos, también se debe crear nuevos métodos selectores y mutadores para estos atributos. Por esta razón, en la clase Clinica corresponde crear los siguientes métodos: import java.util.arraylist; public class Clinica private String nombre; private ArrayList<Paciente> listapac; public Clinica() // Esta lista se crea sin elementos // inicialmente. this.listapac = new ArrayList<Paciente>(); public ArrayList<Paciente> getlistapac() return this.listapac; public void setlistapac(arraylist<paciente> v) this.listapac = v;
... y en la clase Paciente corresponde crear estos otros métodos (selector y mutador): public class Paciente private String nombre; private byte edad; private Enfermedad[] arrenf; public Paciente() // Al crear el arreglo, tendrá 0 objetos. this.arrenf = new Enfermedad[20]; public Enfermedad[] getarrenf() return this.arrenf; public void setarrenf(enfermedad[] v) this.arrenf = v; Con estos selectores y mutadores especiales (que trabajan con la lista de pacientes y el arreglo de enfermedades, respectivamente), el programa en Java puede acceder a los pacientes y enfermedades de cada objeto. Recuerde que es necesario crearlos, ya que al estar declarados con acceso private, no pueden accederse directamente en el programa, sólo a través de estos métodos antes indicados.
Luego, si se desea realizar alguno de los siguientes accesos, haga la llamada correspondiente desde el programa: a) acceder a un objeto de la clase Clinica: Clinica cli = new Clinica(); // Utilice el objeto 'cli' b) acceder al listado de pacientes de la clínica 'cli': cli.getlistapac() c) acceder a un paciente de la clínica 'cli' (al paciente de la posición 4, si es que existe): cli.getlistapac().get(4) d) acceder a la lista de enfermedades del paciente 4 de la clínica 'cli' (siempre que el paciente de la posición 4 exista): cli.getlistapac().get(4).getarrenf() e) acceder a la enfermedad 2 del paciente anterior: cli.getlistapac().get(4).getarrenf()[2] f) obtener el nombre de la enfermedad anterior: cli.getlistapac().get(4).getarrenf()[2].getnombre()
Más códigos de acceso, utilizando ahora métodos mutadores! g) cambiar el nombre de la clínica 'cli': cli.setnombre("nuevonombre"); h) cambiar el nombre del paciente 15 de la clínica 'cli' (si es que el objeto Paciente existe): cli.getlistapac().get(15).setnombre("nompaciente"); i) cambiar los síntomas de la enfermedad 4 del paciente 45 de la clínica 'cli': cli.getlistapac().get(45).getarrenf()[45].setsintomas("nuevossintomas");