Sei sulla pagina 1di 38

Tema 15.

Colecciones

1 Java Collections Framework

2. java.util.Collection

3. java.util.Iterator

4. java.util.Set

5. java.util.List

6 java.util.Map

7. Generics

8. Importancia de los métodos equals() y hashCode()

9. Arrays vs. Colecciones

Ejercicio I

Ejercicio II

Ejercicio III

Referencias:

http://vayajava.blogspot.com.es/2008/05/diferencias-entre-las-colecciones-list.html

Vídeo “Univ. Politécnica de Valencia” https://www.youtube.com/watch?v=zXZ1qSeuO90

1-38
Tema 14. Colecciones

1 Java Collections Framework

Una colección es simplemente un objeto que agrupa varios elementos en uno solo. Se
utilizan para guardar y manipular datos.

Normalmente, los objetos incluidos en ellas suelen ser del mismo tipo, aunque no
necesariamente, depende de si son o no genéricas.

Las colecciones se diferencian de los arrays en que su tamaño no es fijo, esto es, son
dinámicas. Se pueden realizar operaciones de incluir, eliminar, obtener, encontrar o
recorrer una colección.

Las colecciones se organizan a través de tres interfaces que definen los tipos de
estructura (Map, Set y List) y otras, interfaces de soporte, que definen algunas
operaciones generales, como Iterator. A continuación se muestra su organización
jerárquica y una forma rápida de saber cual podría ser la colección elegida en cada caso.

2-38
En Java tenemos un framework de colecciones (Java Collections Framework), implementado
mediante:

- Interfaces, representaciones abstractas de las colecciones que permiten usarlas sin conocer
sus detalles. Las más importantes: List, Set y Map.

- Interfaces de soporte como Iterator (permite recorrer lists en ambos sentidos) o


Comparable (permite comparar y ordenar los elementos de la colección).

- Clases abstractas que tienen total o parcialmente implementados los métodos de la


interface correspondiente.

- Implementaciones: colecciones concretas. Implementaciones de las interfaces que nos


permiten almacenar y manipular grupos de datos como una sola unidad (una colección).
(TreeSet, HashSet, LinkedHashSet, ArrayList, LinkedList, Vector, HashMap, TreeMap,
HasTable y LinkedHasMap).

- Algoritmos: métodos que permiten realizar operaciones como búsquedas, ordenaciones,


etc...

Las interfaces Set y List heredan de la interface Collection, hay una clara diferenciación en cuanto
si permite duplicados y acceso por indice.
Las colecciones que implementan la interface java.util.List permiten duplicados y acceso
por indice.
Las colecciones que implementan la interface java.util.Set no permiten duplicados y acceso
por indice.

3-38
En el caso de los Map, se utilizan siempre que necesitemos trabajar con parejas de datos clave/valor.

Todas las colecciones se encuentran en el paquete java.util.*; siendo java.util.Collection y


java.util.Map las raíces de la jerarquía de las colecciones.

Existirán especializaciones que permitan elementos duplicados o no, que permitan ordenar los
elementos o no, etc.

Estas interfaces (java.util.Collection y java.util.Map ) contiene la definición de todos los métodos


genéricos que deben implementar las colecciones.

4-38
2. java.util.Collection

Los métodos básicos de este interfaz son:

• int size(); // Indica el número de elementos que contiene.

• boolean isEmpty(); // Indica sí no contiene ningún elemento.

• boolean contains(Object element); // Indica sí contiene ese


elemento.

• boolean add(Object element); // Añade un elemento. Devuelve un boolean para indicar si ha


tenido éxito o ha tenido problemas.

• boolean remove(Object element); // Borra un elemento.

• Iterator iterator(); // Devuelve una instancia de Iterator.

Los métodos que corresponden a operaciones masivas son:

• boolean containsAll(Collection c); // Indica sí contiene todos esos elementos.

• boolean addAll(Collection c); // Añade todos los elementos del parámetro pasado. Devuelve
un boolean para indicar si ha tenido éxito o ha tenido problemas.

• boolean removeAll(Collection c); // Borra todos los elementos del parámetro pasado.
Devuelve un boolean para indicar si ha tenido éxito o ha tenido problemas.

• boolean retainAll(Collection c); // Borrar todos los elementos menos los pasados en el
parámetro. Devuelve un boolean para indicar si ha tenido éxito o ha tenido problemas.

• void clear(); // Borra todos los elementos.

Los métodos que corresponden a operaciones con arrays son:

• Object[] toArray(); // Devuelve un array con todos los elementos.

• Object[] toArray(Object a[]); // Devuelve un array con todos los elementos. El tipo será el
del array enviado.

Hay que tener siempre en cuenta que las colecciones no permiten el uso de tipos primitivos. Por
tanto, siempre que necesitemos trabajar con ellos habrá que hacer uso de los Wrappers de Tipos
Primitivos.

5-38
3. java.util.Iterator

El interfaz Iterator representa un componente que permite iterar o recorrer los elementos de una
colección.

Todas las colecciones ofrecen una implementación de Iterator por medio del método:

public Iterator iterator();

Sus métodos son:

• boolean hasNext(); // Indica sí tiene más elementos desde la posición en la que se encuentra
el iterador.

• Object next(); // Devuelve el primer elemento y se queda apuntando al siguiente. En la


siguiente llamada, devolverá el segundo elemento y apuntará al siguiente y así
sucesivamente hasta el final de la colección que devolverá un null.

• void remove(); // Elimina el primer elemento y se queda apuntando al siguiente. En el caso


de que no estemos en la primera posición, borrará el elemento donde esté el iterador y
apuntará al siguiente.

En versiones antiguas de Java existía otro interfaz para recorrer los elementos de la colección,
java.util.Enumeration, pero ya ha dejado de utilizarse. La interface “Enumeration” ha sido revisada
y ampliada por “Iterator”.

6-38
4. java.util.Set

El interfaz Set hereda del interfaz Collection. Pero no añade la definición de ningún método nuevo.

Representa colecciones que no permiten tener elementos duplicados. Para saber si un elemento está
duplicado, hace uso del método:
public boolean equals(Object o);

Existen distintas implementaciones de este interfaz:

java.util.HashSet:

Ofrece el acceso más rápido cuando dicho acceso es aleatorio (no secuencial). Su orden de
iteración es impredecible.

Es la implementación más empleada debido a su rendimiento y a que, generalmente, no nos


importa el orden que ocupen los elementos.

Set <genérico> nombre_colección = new HashSet<Génerico>();

HashSet <genérico> nombre_colección = new HashSet<Génerico>();

java.util.LinkedHashSet:

Almacena los elementos en función del orden de inserción. Es, simplemente, un poco más
costosa (en tiempo) que HashSet.

Su orden de iteración es el orden de inserción.

Set <genérico> nombre_colección = new LinkedHashSet<génerico>();

LinkedHashSet <genérico> nombre_colección = new LinkedHashSet<génerico>();

java.util.TreeSet:

Su orden de iteración depende de la implementación que los elementos hagan del


interfaz java.lang.Comparable, mediante la implementación del método:

public int compareTo(Object o);

Es bastante más lento que HashSet. Los elementos almacenados deben implementar la
interfaz Comparable.

Set <genérico> nombre_colección = new TreeSet <génerico>();

TreeSet <genérico> nombre_colección = new TreeSet <génerico>();

7-38
Ejemplo 01. Guarda en una colección llamada “ciudades” seis objetos de tipo String que
representan nombres de ciudades, prueba con una colección de tipo HashSet, LinkedHashSet,
TreeSet.

Las tres pruebas (TestHashSet, TestLinkedHashSet, TestTreeSet) una para cada tipo de colección,
deberán tener los mismos datos, por ejemplo:

Madrid
Barcelona
Málaga
Vigo
Sevilla
Madrid //repetido

Prueba si admite duplicados.

Escribe su contenido.

Prueba cada colección en una clase distinta, por ejemplo:

import java.util.*;
public class TestHashSet {
public static void main(String[] args) {
HashSet <String> ciudades = new HashSet<String>();

//añade los elementos a la colección

//recorre la colección, imprime su contenido


}
}

import java.util.*;
public class TestLinkedHashSet {
public static void main(String[] args) {
...
}
}

import java.util.*;
public class TestTreeSet {
public static void main(String[] args) {
...
}
}

8-38
Ejemplo 1, vamos a guardar en una colección HashSet, LinkedHashSet y TreeSet seis objetos de
tipo String que representan nombres de ciudades.

Ejemplo: Utilización de java.util.HashSet

import java.util.*;
public class TestHashSet {
public static void main(String[] args) {
HashSet ciudades = new HashSet();
ciudades.add("Madrid");
ciudades.add("Barcelona");
ciudades.add("Malaga");
ciudades.add("Vigo");
ciudades.add("Sevilla");
ciudades.add("Madrid"); // Repetido.
Iterator it = ciudades.iterator();

while(it.hasNext())
System.out.println("Ciudad: " + it.next());
}
}

Ejemplo: Utilización de java.util.LinkedHashSet

import java.util.*;
public class TestLinkedHashSet {
public static void main(String[] args) {
LinkedHashSet ciudades = new LinkedHashSet();
ciudades.add("Madrid");
ciudades.add("Barcelona");
ciudades.add("Malaga");
ciudades.add("Vigo");
ciudades.add("Sevilla");
ciudades.add("Madrid"); // Repetido.
Iterator it = ciudades.iterator();

while(it.hasNext())
System.out.println("Ciudad: " + it.next());
}
}

9-38
Ejemplo: Utilización de java.util.TreeSet

import java.util.*;
public class TestTreeSet {
public static void main(String[] args) {
TreeSet ciudades = new TreeSet();
ciudades.add("Madrid");
ciudades.add("Barcelona");
ciudades.add("Malaga");
ciudades.add("Vigo");
ciudades.add("Sevilla");
ciudades.add("Madrid"); // Repetido.
Iterator it = ciudades.iterator();

while(it.hasNext())
System.out.println("Ciudad: " + it.next());
}
}

10-38
Ejemplo 02. Realiza una aplicación que nos permita mantener una colección de nombres de
ciudades a través del siguiente menú.

Colección de ciudades.
1. Añadir una ciudad.
2. Borrar una ciudad.
3. Vaciar la colección.
4. Escribir el contenido de la colección.
5. Salir.
Elige la opción (1/2/3/4/5).

La aplicación deberá avisar de los siguientes errores:

- Añadir: la ciudad ya existe.


- Borrar: la ciudad no existe.
- Vaciar: la colección se encuentra vacía
- Escribir: la colección se encuentra vacía

Los errores los vamos a tratar como excepciones. Crea las excepciones:

✗ ExcepcionIncluirCuidadYaExiste
✗ ExcepcionBorrarCiudadNoExiste
✗ ExcepcionVaciarNoExistenCiudades
✗ ExcepcionEscribrNoExistenCiudades

Cuando se produzca una excepción no se debe terminar la ejecución del programa, se retorna
siempre al menú.

Teniendo en cuenta que no es relevante su orden de iteración elige el tipo de colección más
adecuado.

Crea una clase que contenga la colección y los métodos: incluirCiudad, borrarCiudad,
vaciarColeccion y escribirColección. Los métodos podrán lanzar una de las excepciones detalladas
anteriormente, realizar el tratamiento de las excepciones en la aplicación (main).

Ejemplo03. Crea una clase de prueba de los métodos que corresponden a operaciones masivas de
las colecciones de tipo Set.

Utiliza dos o más colecciones de nombres de ciudades de tipo HashSet para probar los métodos:

• boolean containsAll(Collection c); // Indica sí contiene todos esos elementos.


• boolean addAll(Collection c); // Añade todos los elementos del parámetro pasado. Devuelve
un boolean para indicar si ha tenido éxito o ha tenido problemas.
• boolean removeAll(Collection c); // Borra todos los elementos del parámetro pasado.
Devuelve un boolean para indicar si ha tenido éxito o ha tenido problemas.
• boolean retainAll(Collection c); // Borrar todos los elementos menos los pasados en el
parámetro. Devuelve un boolean para indicar si ha tenido éxito o ha tenido problemas.

11-38
5 java.util.List

El interfaz List hereda del interfaz Collection. Representa colecciones con elementos
en secuencia. Es decir, con orden.

Permite tener duplicados.

Es accesible mediante índice, de manera que se puede:

• Acceder a un elemento concreto de una posición.


• Insertar un elemento en una posición concreta.

Los métodos que añade este interfaz, para acceso posicional son:

 Object get(int index); // Devuelve el elemento de la posición indicada en el parámetro.

 Object set(int index, Object element); // Reemplaza el elemento de la posición indicada


en el parámetro, con el elemento del parámetro.

 void add(int index, Object element); // Inserta el elemento pasado por parámetro, en la
posición pasada por parámetro.

 Object remove(int index); // Elimina el elemento de la posición indicada por parámetro.


Devuelve el objeto borrado.

 boolean addAll(int index, Collection c); // Inserta todos los elementos pasados por
parámetro (una colección), en la posición pasada por parámetro.

Los métodos que añade este interfaz para operaciones de búsqueda son:

 int indexOf(Object o); // Devuelve la posición de la primera ocurrencia del elemento


pasado por parámetro o -1 si el elemento no se encuentra en la colección.

 int lastIndexOf(Object o); // Devuelve la posición de la última ocurrencia del elemento


pasado por parámetro.

Los métodos que añade este interfaz para obtener subcolecciones son:

 List subList(int from, int to); // Devuelve una lista con los elementos comprendidos entre
las posiciones pasadas por parámetro.

12-38
A continuación se enumeran las distintas implementaciones de este interfaz:

 java.util.ArrayList:

Ofrece un tiempo de acceso óptimo cuando dicho acceso es aleatorio.

 java.util.LinkedList:

Ofrece un tiempo de acceso óptimo cuando dicho acceso es para añadir o eliminar
elementos del comienzo y final de la lista (típico para pilas y colas).

Para aprovechar las ventajas que tenemos en el coste temporal al trabajar con los
extremos de la lista, se proporcionan métodos propios para acceder a ellos en tiempo
constante:

void addFirst(Object obj) / void addLast(Object obj)

Añade el objeto indicado al principio / final de la lista respectivamente.


Object getFirst() / Object getLast()

Obtiene el primer / último objeto de la lista respectivamente.


Object removeFirst() / Object removeLast()

Extrae el primer / último elemento de la lista respectivamente, devolviéndonos dicho


objeto y eliminándolo de la lista.

 java.util.Vector:

Es como el ArrayList, pero sincronizado, lo que penaliza notablemente el rendimiento.


La sincronización es importante cuando más de un thread (hilo de ejecución) va a
acceder a la colección.

En la práctica la mayoría de las ocasiones es mejor usar ArrayList porque el tiempo de las
operaciones y uso de memoria es menor que en LinkedList, de manera simple: si no sabes cual
usar usa ArrayList.

13-38
Ejemplo 04. Guarda en una colección llamada “ciudades” seis objetos de tipo String que
representan nombres de ciudades, prueba con una colección de tipo ArrayList, LinkedList y
Vector.

Las tres pruebas, una para cada tipo de colección, deberán tener los mismos datos, por ejemplo:

Madrid
Barcelona
Malaga
Vigo

- Inserta el elemento “Sevilla” en la posición 1.

- Prueba si admite duplicados, introduce otra vez “Madrid”.

Escribe su contenido.

Muestra el contenido de la colección.

Prueba cada colección en una clase distinta (TestArrayList, TestLinkedList y TestVector).

import java.util.*;
public class TestArrayList {
public static void main(String[] args) {

//crea la colección ciudades de tipo ArrayList


ArrayList <String> ciudades = new ArrayList <String>();

//añade los elementos a la colección

//inserta el elemento “Sevilla” en la posición 1

// prueba si admite duplicados, introduce otra vez “Madrid”

//recorre la colección, imprime su contenido

}
}

14-38
6 java.util.Map

El interfaz Map no hereda del interfaz Collection. Representa colecciones con parejas de
elementos: clave y valor; de tal manera que para una clave solamente tenemos un valor. Esta
estructura de datos también es conocida en otros lenguajes de programación como "Diccionarios".

Ejemplo:

tipo_map <clave, valor> identificador = new tipo_coleccion <clave, valor>();

Siendo clave y valor objetos.

HashMap <String, Estudiante> curso= new HashMap <String, Estudiante>();

No permite tener claves duplicadas. Pero si valores duplicados.

Para calcular la colocación de un elemento se basa en el uso del método:

public int hasCode();

En Java un HashCode ver wiki:


http://es.wikipedia.org/wiki/HashCode%28%29_%28Java%29

Los métodos básicos de este interfaz son:

 Object put(Object key, Object value); // Inserta una pareja de clave/valor pasados como
parámetros.

Devuelve el valor que previamente tuviera asociada la clave (key) si ya estaba en el Map, en caso
contrario, es decir, que no exista la clave (key), devuelve null. Es decir, una operación correcta de
insercción de un valor devuelve null.

 Object get(Object key); // Devuelve el valor de su clave pasada como parámetro.

Si la clave no existe devuelve null, en caso contrario devuleve su valor asociado.

 Object remove(Object key); // Elimina una pareja de clave/valor, que corresponde a la


clave que se pasa como parámetro.

Si la clave existe se realiza la operación de borrado y el método devuelve el valor borrado, si la clave no
existe devuelve null.

 boolean containsKey(Object key); // Comprueba la existencia de una clave pasada como


parámetro.

 boolean containsValue(Object value); // Comprueba la existencia de un valor pasado


como parámetro.

 int size(); // Indica el número de pareja que contiene.

 boolean isEmpty(); // Indica sí no contiene ninguna pareja.

15-38
Los métodos que realizan operaciones masivas son:

 void putAll(Map t); // Añade todas las parejas que existen en el parámetro t.

 void clear(); // Elimina todas las parejas de clave/valor.

Los métodos que realizan operaciones de obtención de colecciones son:

 public Set keySet(); // Devuelve las claves en un java.util.Set.

 public Collection values(); // Devuelve los valores en un java.util. Collection.

A continuación enumeramos las distintas implementaciones de este interfaz:

 java.util.HashMap:

Ofrece un tiempo de acceso óptimo cuando dicho acceso es aleatorio. Su orden de


iteración es imprevisible.

 java.util.Hashtable:

Es la versión sincronizada de HashMap.

 java.util.LinkedHashMap:

Su orden de iteración es el de inserción.

 java.util.TreeMap:

Su orden de iteración depende de la implementación que los elementos (claves) hagan


del interfaz java.lang.Comparable, mediante la implementación de su método public
int compareTo(Object o);

16-38
Ejemplo 05. Utilizando las cuatro tipos de estructuras anteriores (colecciones de
parejas de elementos) inserta los elementos en una colección denominada
“codigos”:

Claves Valores
01 Urgente
02 Importante
03 Normal
04 Baja Prioridad

Accede por su clave al elemento “03” y “01”

Recorre de forma secuencial la colección: escribe la clave y el valor de cada


elemento.

¿Encuentras las diferencias: TestHashMap, TestHashtable, ...?

public class TestHashMap{

public static void main(String[] args){

//define la colección “codigos” de tipo HashMap


HashMap <String, String> codigos = new HashMap <String, String>();

//inserta los elementos

...

//obtener el valores del elemento “03” y “01”

//recorre la colección: escribe la clave y el valor de cada elemento

...
}

17-38
7. Generics

Se trata de una de las grandes novedades que introdujo Java SE1 5.0

Permite tipar el contenido de las colecciones, de manera que se restringe el tipo utilizado y se
elimina el uso masivo de castings.

Para definir el tipo del contenido de una colección se utiliza <tipo>:

tipo_coleccion <tipo_contenido> identificador = null;


identificador = new tipo_coleccion<tipo_contenido>();

Ejemplo1: Suponemos definida una clase Estudiante

//sin definir tipo

ArrayList curso = new ArrayList();

Estudiante varEstudiante = (Estudiante) curso.get(0);

//con tipo definido

ArrayList <Estudiante> curso = new ArrayList<Estudiante>();

// En este caso, no es necesario hacer el casting:

Estudiante varEstudiante = curso.get(0);

En el caso de colecciones de tipo Map, se definiría tanto el tipo de las claves como el de los
valores:

Map<Integer,String> mapa = new HashMap<Integer,String>();

Cuando se crea un objeto de tipo Iterator también es conveniente definir el tipo para evitar
casting.

Iterator <Estudiante> it = ciudades.iterator();


while(it.hasNext()) {
varEstudiante= it.next();
}

1Java Platform, Standard Edition o Java SE (conocido anteriormente hasta la versión 5.0 como
Plataforma Java 2, Standard Edition o J2SE), es una colección de APIs del lenguaje de
programación Java útiles para muchos programas de la Plataforma Java.

18-38
8. Importancia de los métodos equals() y hashCode()

El método equals es muy importante para trabajar con colecciones, debe definirse para que, por
ejemplo, reconozca duplicados y puedan utilizarse algunos métodos de forma correcta como
contains(), add() y remove().

Para saber si un objeto está contenido en una colección se va llamando al método equals() de todos
los objetos de la colección. Para borrarlo de una colección, se le busca de igual forma. Y para
añadirlo en un Set que no permite duplicados, lo mismo.

Los métodos de la interface Collection, como add (elemento), contains(elemento) o


remove(elemento):

• boolean add(Object element)

• boolean contains(Object element);

• boolean remove(Object element);

Los que buscan un elemento dentro de la colección en la interface List

• int indexOf(Object o);


• int lastIndexOf(Object o);

Si se sobre-escribe el método equals se debe sobre-escribir el método hashCode:

“Note that it is generally necessary to override the hashCode method whenever this method is
overridden, so as to maintain the general contract for the hashCode method, which states that equal
objects must have equal hash codes.” Java API

En los siguientes videos puedes encontrar información:

https://www.youtube.com/watch?v=b1htaYhRawk

https://www.youtube.com/watch?v=nJBNMN4Dwss

19-38
8.1 Método hashCode()

En las colecciones de objetos propios (no son objetos de la API de Java, como String, Integer,
Double, …) es conveniente implementar los métodos equals() y hashCode().

Y siempre que creemos nuestras propias claves para el uso de los Map (objetos propios),
debemos sobreescribir obligatoriamente los métodos equals() y hashCode() para dicha clave.

El motivo es que los Map utilizan estos dos métodos para llevar a cabo tanto las inserciones
como las extracciones de valores.

Para entender mejor el uso de estos dos métodos por parte de los Map, veamos un poco más
en detalle la estructura interna de este tipo de colección y el significado de Hash.

Una estructura de datos tipo hash, tabla hash, matriz asociativa, mapa hash, tabla de
dispersión o tabla fragmentada es una forma de organizar datos en memoria principal, que asocia
claves (keys) con valores. Permite el acceso al valor almacenados a partir de una clave.

Funciona transformando la clave con una función hash2 en un hash, un número que identifica
la posición (casilla o cubeta) donde la tabla hash localiza el valor deseado.

La estructura interna de un Map es la siguiente:

Un Map internamente contiene una secuencia


de compartimentos (buckets) donde se van
almacenando todos los valores (clave/valor).

Para decidir en qué compartimento se


almacena un valor, se llama al método
hashCode() del objeto utilizado como clave.

2 Función hash o Hash: http://es.wikipedia.org/wiki/Funci%C3%B3n_hash

20-38
La implementación del método hashCode() debe cumplir las siguientes normas:

 La ejecución sucesiva del método hashCode() sobre un mismo objeto sin haber
modificado su estado interno entre medias, debe devolver siempre el mismo código
hash.

 Si x.equals(y) devuelve true, entonces tanto x como y deben generar el mismo código
hash.

 Sin embargo, si x.equals(y) devuelve false, no es obligatorio que tanto x como y


deban generar un código hash distinto. No obstante es lo deseable para evitar en la
medida de lo posible las colisiones en los Map y por tanto ofrecer un mejor rendimiento.

La implementación del método hashCode() no es una tarea trivial. No obstante, aquí


proponemos dos sugerencias bastante sencillas:

 Convertir a String los valores de los distintos atributos de la clase. Concatenarlos y


delegar la generación del código hash en el método hashCode() del String resultante (la
clase String posee una implementación bastante eficaz del método hashCode()).

 Sumar el código hash de cada uno de los atributos de la clase (los wrappers de tipos
primitivos también tienen sobreescrito el método hashcode()).

21-38
Ejemplo: Sobreescritura del método hashcode

public class TestHashCode{


private int valor1;
private Integer valor2;
public TestHashCode(int valor1, Integer valor2) {
this.valor1 = valor1;
this.valor2 = valor2;
}
public int hashCode(){
StringBuffer buffer = new StringBuffer();
buffer.append(Integer.toString(valor1));
buffer.append(valor2.toString());
return buffer.toString().hashCode();
}
}

public static void main(String[] args){


TestHashCode test1 = new TestHashCode(1, new Integer(2));
TestHashCode test2 = new TestHashCode(1, new Integer(2));
System.out.println(test1.hashCode());
System.out.println(test2.hashCode());
}

22-38
Método equals.

Pueden ocurrir colisiones, es decir, que un compartimento ya esté utilizado por una pareja
clave/valor. Esto puede ser debido a que:

 Dos objetos distintos devolvieron el mismo código hash.

Imaginemos que hacemos un get del Map y el algoritmo hash correspondiente tiene colisiones.
¿Qué valor nos devuelve?

Lo sabe mediante el uso del método equals() de la clave. Va iterando por todas las claves de
ese compartimento para encontrar la que se ha pedido.

Imaginemos que hacemos un put en el Map con una clave ya existente. ¿Cómo sabe que ya
existe y que hay que machacar el valor anterior?

Lo sabe mediante el uso del método equals() de la clave. Itera para comprobar si ya existe.

La implementación del método equals() debe cumplir las siguientes normas:

 Reflexiva: x.equals(x) debe devolver true.

 Simétrica: Si x.equals(y) devuelve true, y.equals(x) debe devolver también true.

 Transitiva: Si x.equals(y) devuelve true, e y.equals(z) devuelve true, x.equals(z) debe


devolver también true.

 Consistente: Si x.equals(y) devuelve true, entonces las sucesivas invocaciones de x.equals(y)


sin haber modificado el estado de x o y deben seguir devolviendo true.

 Null: x.equals(null) siempre debe devolver false.

Ejemplo: Sobreescritura del método equals


public class TestEquals{
private int valor1;
private Integer valor2;
public TestEquals(int valor1, Integer valor2) {
this.valor1 = valor1;
this.valor2 = valor2;
}
public boolean equals(Object o){
if(this == o) // Primer paso.
return true;
if(!(o instanceof TestEquals)) // Segundo paso.
return false;
TestEquals param = (TestEquals)o; // Tercer paso.
return param.valor1 == valor1 && param.valor2.equals(valor2);
}
}

public class Prueba {


public static void main(String[] args){
TestEquals test1 = new TestEquals(1, new Integer(2));
TestEquals test2 = new TestEquals(1, new Integer(2));
System.out.println(test1.equals(test2));
}

23-38
El método equals() también es importante para el resto de Colecciones.

Por ejemplo, ¿cómo funcionan los métodos contains(), add() y remove() de las colecciones?

Para saber si un objeto está contenido en una colección se va llamando al método equals() de
todos los objetos de la colección. Para borrarlo de una colección, se le busca de igual forma. Y
para añadirlo en un Set que no permite duplicados, lo mismo.

Ejemplo: Sobreescritura del método equals

public static void main(String[] args){


TestEquals test1 = new TestEquals(1, new Integer(2));
List list = new ArrayList();
list.add(test1);
TestEquals test2 = new TestEquals(1, new Integer(2));
System.out.println(list.contains(test2));
}

https://www.youtube.com/watch?v=b1htaYhRawk

24-38
9. Elegir un tipo de Colección

Fuente:
https://www.adictosaltrabajo.com/tutoriales/introduccion-a-colecciones-en-java/

En el

siguiente enlace puedes encontrar una información más detallada:


https://www.luaces-novo.es/guia-de-colecciones-en-java/

25-38
10. Arrays vs. Colecciones

A continuación mostraremos un resumen de las características tanto de los Arrays como de las
Colecciones, que nos permitirán poder discernir cuando utilizar un tipo u otro.

Las características de los Arrays son:

 Tamaño estático.

 Su tamaño se conoce mediante el atributo length.

 Puede almacenar tanto tipos primitivos como tipos complejos.

 Solo pueden albergar elementos de un tipo.

Las características de las Colecciones son:

 Tamaño dinámico.

 Su tamaño se conoce mediante el método size().

 Solo puede almacenar tipos complejos.

 Puede albergar elementos de distinto tipo.

26-38
Ejercicio 1 . Un sistema de gestión de pacientes.

Tendremos un archivador donde iremos guardando todas las fichas de los pacientes.

Las fichas (clase Ficha) contienen la siguiente información: nombre, apellidos y fecha de
nacimiento.

Todas las fichas que vayamos creando, se podrán guardar o eliminar del archivador.

Al archivador también le podremos pedir un listado. Este listado consistirá en imprimir por
pantalla el número de fichas guardadas, así como el contenido de las fichas.

La clase PracticaUno tiene un método main en el que se crea un archivador (Clase Archivador),
dos o tres fichas que se guardarán en el archivador, se listará el contenido, se eliminará alguna
ficha y se volverá a listar su contenido.

Realizar el diagrama UML.

NOTA: Realizar esta práctica mediante dos tipos de Colecciones: ArrayList y HashSet.

Ejercicio 2. Desarrollar un sistema de gestión de partes de un taller mecánico.

La información de los partes (clase Parte) es: código, descripción e importe. Un parte irá
asociado a la matrícula de un coche de manera que podamos añadir, mostrar y eliminar un
parte a partir de la matrícula de coche al que está asociado.

La clase Practica2 mostrará un menú de opciones por consola: crear, listar, mostrar y eliminar
partes. También habrá una opción de salir.

Para leer por teclado se usará la clase Lector que se adjunta con esta práctica.

Realizar el diagrama UML.

Ejercicio 3. Desarrollar un sistema de gestión de partes de un taller mecánico.

La información de los partes (Clase Parte) es: código, descripción e importe. Un parte irá asociado a la
matrícula de un coche de manera que podamos añadir, mostrar y eliminar un parte a partir de la matrícula
de coche al que está asociado. La información de una matrícula (Clase Mátricula) es: provincia, número y
letra.

La clase Practica3 mostrará un menú de opciones por consola: crear, listar, mostrar y eliminar partes.
También habrá una opción de salir.

El formato de entrada de una matrícula es: provincia-número-letra (por ejemplo: M-1234-AV).El sistema de
matriculación provincial estuvo vigente en España hasta el 18 de septiembre de 2000, cuando entró
en vigor el actual sistema. Siguiendo como referencia este sistema debemos añadir las siguientes
comprobaciones:

Comprobar que la primera y última parte de la matricula tiene como máximo 2 letras.

Realizar el diagrama UML.

27-38
Ejercicio 4 . Sistema de gestión de pacientes mejorado

Realiza la siguientes mejoras sobre la aplicación del ejercicio 1:

- Añade un campo que identifique de forma inequivoca a un paciente, DNI. Realiza los cambios
que procedan.

- Implementa las siguientes excepciones:

• ExcepcionGuardaFicha, se lanza cuando se quiere guardar una ficha (paciente) que ya


existe en la colección.

• ExcepcionEliminaFicha, se lanza cuando se desea eliminar una ficha (paciente) que no


existe en la colección.

La aplicación no deberá terminar cuando se produzcan las excepciones descritas


anteriormente, se informará al usuario de la excepción producida y continua la ejecución del
programa.

Realiza una aplicación que pruebe todas las funcionalidades.

NOTA: Realiza esta práctica utilizando la Colección: HashSet.

28-38
Ejercicio 5. Sistema de gestión de partes de un taller mecánico mejorado.

Sobre el ejercicio 3 realiza las siguientes modificaciones:

1. Controla posibles excepciones en la entrada de información incorrecta, utiliza la clase


Scanner para las operaciones de entrada por consola.

2. La clase Matricula debe comprobar en su constructor posibles errores de matrícula


incorrecta para lo que lanzará las excepciones oportunas:

ExcepcionProvincia: La provincia (primera parte de la matricula) puede ser uno o dos


caracteres, sus valores se recogen en un array de String:

String matProv []= {"VI", "AB", "A", "AL", "AV", "BA", "IB", "B", "BU", "CC", "CA",
"CE", "CS", "CR", "CO", "C", "CU", "GI", "GR", "GU", "SS", "H", "HU", "J", "LE", "L",
"LO", "LU", "M", "MA", "ML", "MU", "NA", "OU", "O", "P", "GC", "PO", "SA", "TF", "S",
"SG", "SE", "SO", "T", "TE", "TO", "V", "VA", "BI", "Z", "ZA"};

ExcepcionNumero: El número de la matrícula debe tener 4 dígitos.

ExcepcionLetra: Las letras de la matrícula (última parte de la matricula con formato provincial) tiene
como máximo 2 letras.

La provincia y las letras de la matrícula se guardarán en mayúsculas.

3. En la clase Taller se tiene que controlar la siguientes excepciones:

ExcepcionMatriculaExiste, se desea crear un elemento de la colección Map <Matricula,


Parte>con un valor de matrícula que ya existe en la colección.

ExcepcionMatriculaNoExiste, se desea eliminar o consultar un elemento de la colección Map


<Matricula, Parte> con un valor de matrícula que no existe en la colección.

4. Sobre la aplicación:

• Deberá comprobar que la matrícula es correcta antes de operar con un parte.

• Su ejecución no terminará cuando se produzcan excepciones, permitiendo al


usuario después del aviso de la excepción, introducir un valor nuevo de la
matrícula para completar el proceso según proceda.

29-38
Ejercicio 6. Nueva versión : Sistema de Gestión de Pacientes de un Hospital.

Crear un proyecto en Java que nos permita recoger, guardar y gestionar la información de los pacientes de
un Hospital.

Se desea guardar de todos los pacientes su nif, nombre, apellidos y fecha de nacimiento (dd/mm/yyyy).

El hospital tiene dos tipos de pacientes:


• Externos, los que acuden a consulta o revisión.
• Internos, los que ingresan en el hospital.

De los pacientes externos se debe informar de la fecha (dd/mm/yyyy hh:mm) y la especialidad de su


consulta o revisión. Las especilidades del hospital deben recogerse en una clase de tipo enumerada:

1. ANESTESIOLOGÍA.
2. CARDIOLOGÍA.
3. CIRUGÍA GENERAL
4. CIRUGÍA VASCULAR
5. DERMATOLOGÍA
6. GINECOLOGÍA
7. MEDICINA FAMILIAR
8. NEUMONOLOGÍA
9. ALERGOLOGÍA
10. REUMATOLOGÍA

De los pacientes internos se debe guardar la siguiente información, según proceda, la fecha de ingreso, el
área hospitalaria del ingreso, la fecha del alta, la causa del alta y la dirección de traslado. Descripción de la
información:

- La fecha de ingreso (dd/mm/yyyy hh:mm) es la fecha del sistema informático según ingreso de paciente.
- El área hospitalaria, debe de ser una de las siguientes valores:

1. CARDIOLOGÍA
2. CUIDADOS INTENSIVOS
3. DIGESTIVO
4. PEDIATRÍA
5. NEUMOLOGÍA
6. ONCOLOGÍA
7. MEDICINA INTERNA
8. CIRUGÍA GENERAL Y DIGESTIVA
9. CIRUGÍA ORTOPÉDICA Y TRAUMATOLOGÍA

Todas las áreas deben recogerse en una clase de tipo enumerada.

- La causa del alta, con cuatro únicos valores “Médica”, “Voluntaria”, “Traslado” o “Deceso”. Las causas
deben recogerse en una clase tipo enumerada.

La aplicación deberá realizar los siguientes procedimientos según un menú:

Gestión de pacientes.
1. Ingreso.
2. Alta.
3. Consultas.
4. Salir de la aplicación.

30-38
1. Ingreso de un paciente. Se deberán informar obligatoriamente los campos generales del paciente y los
que procedan según el tipo del paciente.

Datos generales: nif, nombre, apellidos y fecha de nacimiento

Pueden producirse las siguientes excepciones:

- ExcepcionNifIncorrecto: la excepción trata todos los posibles errores de la cadena que recibe el
valor del nif, a saber:
- Debe estar entre 8(7+1) y 9(8+1) caracteres.
- El número del dni debe ser mayor a 999999 y menor a 99999999.
- La letra debe coincidir con el algoritmo de cálculo de la letra para un dni.

- ExcepcionNombreApellido
- Nombre o Apellidos deben de contener sólo letras.

- ExcepcionFechaNacimiento
- La fecha debe ser inferior a la fecha actual.
- No son válidas edades superiores a los 120 años, con respecto a la fecha actual.

Datos según tipo de paciente. Se pedirá al usuario de la aplicación que especifique el tipo de paciente:

Tipo de paciente:
E. Externo (Consultas o revisón)
I. Interno (Ingreso en el hospital)

Pacientes Externos

Se guarda la fecha y la hora de la consulta, y la especilidad. La aplicación deberá comprobar que escribe
una especialidad correcta según un menú, los valores deberán obtenerse de la clase enumerada. La
aplicación deberá tener en cuenta que las especialidades del hospital pueden cambiar (pendiente)

Puede producir la siguiente excepción:

- ExcepcionFechaConsulta, la fecha deberá ser mayor a la fecha actual y no superior a 6 meses


desde la fecha actual.

- ExcepcionPacienteExternoYaExiste, el paciente, la especialidad de la consulta y la cita


(dd/mm/yyyy hh;mm) ya existe.

Pacientes Internos

- La fecha de ingreso (dd/mm/yyyy hh:mm) no se debe solicitar, automáticamente se asigna la fecha del
sistema informático.

- El área hospitalaria debe ser unos de los valores indicados en un menú, la aplicación deberá comprobar
que escribe una especialidad correcta según dicho menú, los valores deberán obtenerse de la clase
enumerada.

El resto de campos del paciente de tipo interno no deberán tomar ningún valor en el procedimiento de
ingreso.

Puede producir la siguiente excepción:

- ExcepcionInterno: el paciente de tipo interno ya se encuentra ingresado.

31-38
Dos instancias de pacientes Internos son iguales si tienen el mismo valor en los campos: nif, fecha de
ingreso y fecha de alta.

2.- Alta de un paciente.

Se solicita el NIF del paciente.

Se busca dicho paciente y se muestra su información.

Se confirma el proceso “Alta del paciente (S/N)”

Si no se confirma el proceso se vuelve al menú principal de la aplicación.

Si se confirma el proceso se solicita:

La fecha de alta (dd/mm/yyyy). La aplicación deberá completar la fecha de alta asignando siempre la
siguiente hora: 12:00.

La causa del alta. Elegir opciones en un menú.

Si la cuasa del alta es “Traslado” debe solicitarse la información de la dirección de traslado.

Se pueden producir las siguientes excepciones:

ExcepcionNIFInexistente, no se encuentra el paciente que se pretende dar de alta.

ExcepcionPacienteAlta, el paciente que se pretende dar de alta ya tiene el alta.

ExcepcionFechaAlta, debe ser igual o superior a la fecha actual.

ExcepcionDireccionTraslado, la cuasa del alta debe ser de tipo “Traslado” para dar valor al campo dirección
de traslado.

3.- Consultas de pacientes. Se podrán obtener los siguientes listados según menú.

1. Pacientes externos pendientes de consulta.

Todos los pacientes externos cuya fecha de consulta o revisión sea mayor o igual a la fecha
actual. Ordenado por especialidad y fecha de consulta.

2. Pacientes internos sin alta.

Todos los pacientes internos sin fecha de alta. Ordenado por área, apellidos y nombre del
paciente.

3. Paciente internos histórico.

Todos los pacientes internos con alta. Ordenado por apellidos, nombre y fecha de alta.

Los listados son una utilidad residual de la aplicación se debe priorizar la rápidez de sus otras funciones.

4- Salir de la aplicación.

Se deberá guardar la colección en un único fichero con la siguiente dirección:

“c:/hospital/coleccion_pacientes.dat”

32-38
Realiza el modelo de datos de la aplicación, refleja las clase de tipo enumerado.

Orientaciones para la realización del proyecto.

- Estudia detenidamente el enunciado y los anexos que se detallan a continuación.

- Dada la complejidad del proyecto se va a realizar en 5 pasos, para cada paso se deberá comprobar su
funcionamiento y realizar una puesta en común en clase.

01 solución (Modelado de datos)


02 solución (Jerarquia de pacientes)
03 solución (Archivo Externo)
03 solución (Archivo Interno)
05 solución (Final)

Además de las excepciones lanzadas por el programa, se deberá tener en cuenta las excepciones que se
puedan producir por una entrada inapropiada de valor por parte del usuario, y su adecuado tratamiento.

Las excepciones tratadas no deben provocar la terminación del programa.

Es muy importante que por cada clase que vamos a crear:

• Se analicen las excepciones que sus métodos puedan generar y se implementen, crear la
excepción y programar su lanzamiento.

• Comprobar el funcionamento de cada clase, prueba unitaria. Si se crea una clase, por ejemplo,
Paciente debemos comprobar todas sus funcionalidades en una clase prueba, por ejemplo,
PruebaPaciente.

33-38
Anexo I. Enumerados.

Fuente:

Uso avanzado de enumerados


http://raulavila.com/2015/02/enums-java/

Enum (Enumerados) en Java, con ejemplos


https://jarroba.com/enum-enumerados-en-java-con-ejemplos/

Curso Java. Tipos enumerados. Vídeo 48


https://www.youtube.com/watch?v=DwriSApbm50

Los tipos enumerados de Java (enum) fueron añadidos en la versión 5 del lenguaje. Su uso más inmediato
fue el de “contenedores de constantes” o “valores finitos” de una variable.

Ejemplo: “demarcaciones en las que puede jugar un futbolista”, son finitas y por tanto se pueden enumerar
en: Portero, Defensa, Centrocampista y Delantero.

Creamos la clase "Enum" llamada "Demarcación":

public enum Demarcacion {


PORTERO, DEFENSA, CENTROCAMPISTA, DELANTERO
}

una instancia del tipo Demarcacion sólo puede tomar esos valores, por ejemplo:

Demarcacion jugador1, jugador2;

jugador1= Demarcacion.DEFENSA;

jugador2= Demarcacion.DEFENSA;

Demarcación es una clase que tiene métodos, constructor y atributos.

Algunos métodos:

jugador1.name();
// Devuelve un String con el nombre de la constante (DELANTERO)

jugador1.toString();
// Devuelve un String con el nombre de la constante (DELANTERO)

jugador1.ordinal();
// Devuelve un entero con la posición del enum según está declarada (3).

jugador1.compareTo(jugador2);
// Compara el enum con el parámetro según el orden en el que están declarados lo enum

Demarcacion.values();
// Devuelve un array de tipo Demarcacion que contiene todos los enum

34-38
Una clase de tipo enum pede tener atributos y constructor, aunque siempre se define como privado, limita la
creación de objetos a los especificados en su clase.

Ejemplo: definimos un enumerado "Equipo" que va a tener dos atributos; el nombre del equipo y el puesto
en el que quedaron en la liga del año 2009/2010.

public enum Equipo {


//enumerados
BARCELONA("FC Barcelona",1),
REAL_MADRID("Real Madrid",2),
SEVILLA("Sevilla FC",4),
VILLAREAL("Villareal",7);

//atributos
private String nombreClub;
private int puestoLiga;

//constructor
private Equipo (String nombreClub, int puestoLiga){
this.nombreClub = nombreClub;
this.puestoLiga = puestoLiga;
}

//obtener el valor del atributo: nombreClub


public String getNombreClub() {
return nombreClub;
}

//obtener el valor del atributo: puestoLiga


public int getPuestoLiga() {
return puestoLiga;
}

// Instanciamos el enumerado
Equipo equipo1 = Equipo.VILLAREAL;

// Devuelve un String con el nombre de la constante


System.out.println("equipo1.name()= "+equipo1.name());

// Devuelve el contenido de los atributos


System.out.println("equipo1.getNombreClub()="+equipo1.getNombreClub());
System.out.println("equipo1.getPuestoLiga()="+equipo1!C.getPuestoLiga());

35-38
Anexo II. Recordatorio de las fechas.

Clase Calendar
Calendar es una clase abstracta, por lo que no podemos hacer un new de ella.

Fecha/Hora actual

Calendar fecha= Calendar.getInstance();

Métodos

public int get(int field)

Recupera los campos que componen la fecha, indicando el campo concreto que se quiere
obtener.

public void set(int field, int value)

Permite modificar el campo de la fecha que se indique en el primer parámetro con el valor
del segundo parámetro.

public long getTimeInMillis()

Devuelve el número de milisegundos que han pasado desde el 1 de Enero de 1970 a las
00:00:00 hasta la fecha/hora representada por nuestra instancia de Calendar.

Constantes

Los siguiente campos de la clase Calendar tiene el modificador de acceso y tipo: public static final int . Un
listado completo lo puedes encontrar en la API de Java, a continuación se detalla su valor y significado de
los más utilizados:

Value Constant Field

1 YEAR Año.
2 MONTH Mes. Entre 0 y 11.
5 DATE, DAY_OF_MONTH Día del mes.
7 DAY_OF_WEEK Día de la semana entre 1 (SUNDAY) y 7
(SATURDAY).
10 HOUR Hora antes o después del medio día (en
intervalos de 12 horas).
11 HOUR_OF_DAY Hora absoluta del día (en intervalos de 24
horas).
12 MINUTE El minuto dentro de la hora.
13 SECOND El segundo dentro del minuto.

36-38
Clase GregorianCalendar

Contructores

GregorianCalendar()

GregorianCalendar(int year, int month, int dayOfMonth)

GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute)

… Un listado completo lo puedes encontrar en la API de Java

Métodos

public void add(int field, int amount)

Al campo de la fecha del primer parámetro le suma el valor del segundo parámetro

Ejemplo: fecha.add(Calendar.MONTH, 2);

public void roll(int field, int amount)

Igual que el método anterior pero sin modificar ningún otro campo cuando se llega al límite superior
o inferior del campo especificado en el parámetro.

Aritmética de fechas

Calendar fecha1 = new GregorianCalendar(2016,00,25); // 25-01-2016


Calendar fecha2 = new GregorianCalendar(2017,02,28); // 28-03-2017
long milisegundos=fecha2.getTimeInMillis()-fecha1.getTimeInMillis();
System.out.println("Milisegundos: "+milisegundos);

El contenido de la variable diferencia es 36975600000.

Para pasar los milisegundos a días debemos

• dividir por 1000 para pasar los milisegundos a segundos


• después dividir por 60 para pasar los segundos a minutos
• después dividir por 60 para pasar los minutos a horas
• después dividir por 24 para pasar las horas a días
El siguiente código nos daría el número de días entre ambas fechas

long dias = milisegundos/(1000*60*60*24);


System.out.println("Días: "+dias);

37-38
Anexo I I I. TRATAMIENTO DE FECHAS EN JAVA 8

https://www.youtube.com/watch?v=xQP-AdCCM7c

Conociendo la nueva Date API en Java 8 (Parte I)


http://blog.eddumelendez.me/2016/07/conociendo-la-nueva-date-api-en-java-8-parte-i/

Conociendo la nueva Date API en Java 8 (Parte II)


http://blog.eddumelendez.me/2016/07/conociendo-la-nueva-date-api-en-java-8-parte-ii/

Trabajando con fechas en Java 8


https://www.solvetic.com/tutoriales/article/1345-trabajando-con-fechas-en-java-8/

38-38

Potrebbero piacerti anche