Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Tabla de contenidos
1 Clases genéricas 3
1.1 Implementación de clases genéricas 3
1.2 Instanciación de clases genéricas 4
1.3 Otras formas de especificar tipos genéricos 4
Tema 9 - Colecciones
2 Colecciones 6
2.1 API Collections (java.util.Collections) 7
2.2 Colecciones lineales (List) 8
2.3 Colecciones sin elementos repetidos (Set) 10
2.4 Colecciones clave-valor (Map) 11
3 Iteradores 14
3.1 Métodos de un iterador 14
3.2 Utilización de iteradores 14
7 Referencias 25
1 Clases genéricas
Se trata de una clase parametrizada sobre uno o más tipos. Las clases genéricas nos evitan
duplicar clases que administran tipos de datos distintos, pero implementan algoritmos o
una lógica similar. Veamos un ejemplo:
Si quisiéramos crear una clase que nos permitiera almacenar objetos de distinto tipo,
podríamos implementar el siguiente código:
Tema 9 - Colecciones
public class Caja {
private Object objeto;
Sin embargo, este tipo de implementaciones son problemáticas, entre otras razones, por
las siguientes:
▪ Como todas las clases heredan de object, es fácil pasar de manera inadvertida un
objeto de una clase que no se espera.
▪ Al recuperar un objeto, nos vemos continuamente obligados a realizar castings.
public T extraer() {
return objeto;
}
}
Tema 9 - Colecciones
▪ E (element, elemento)
▪ K (key, clave)
▪ N (number, número)
▪ T (type, tipo)
▪ V (value, valor)
▪ S, U, V, ... (segundo, tercer, cuarto... tipo)
Este estilo implica escribir demasiado. Desde Java SE 7 tenemos el operador <>
(diamond).
También podemos indicar más de un tipo, aunque solo uno de ellos puede ser una clase.
El resto deben ser interfaces.
public class A {
// Resto de la clase
}
public interface B {
// Resto de la interfaz
}
Los tipos parametrizados nos permiten relajar el tipo concreto de una clase genérica a un
Tema 9 - Colecciones
subtipo. Esto es útil con colecciones. Por ejemplo, la siguiente función espera recibir una
lista de objetos descendientes de la clase Number ( Byte, Double, Float, Integer,
Long, Short).
return suma;
}
También se puede utilizar <? super T> para indicar que el tipo esperado debe ser un
ancestro de otro. De modo que, por ejemplo:
▪ List<? extends T> representa una lista de la que podremos obtener ejemplares
de T.
▪ List<? super T> representa una lista en la que podremos guardar ejemplares de
T.
Con el siguiente ejemplo se puede ver más claramente la diferencia entre <? super T> y
<? extends T>:
2 Colecciones
Una colección en Java es una estructura de datos que permite almacenar muchos valores
del mismo tipo; por tanto, conceptualmente es prácticamente igual que un array. Según el
uso y según si se permiten o no repeticiones, Java dispone de un amplio catálogo de
colecciones: ArrayList (lista), ArrayBlockingQueue (cola), HashSet (conjunto),
Stack (pila), etc.
Las clases que implementan las interfaces de colecciones suelen tener nombres en forma
Tema 9 - Colecciones
de <Implementación-Estilo><Interfaz> . Las implementaciones de propósito general se
resumen en la siguiente tabla:
Tema 9 - Colecciones
Todas las colecciones están definidas como genéricas.
Tipos de colecciones
List
Set
Map
Tema 9 - Colecciones
Interfaz List
La clase ArrayList
Un ArrayList es una estructura en forma de lista que permite almacenar elementos del
mismo tipo (pueden ser incluso objetos); su tamaño va cambiando a medida que se
añaden o se eliminan esos elementos.
Tema 9 - Colecciones
Nos podemos imaginar un ArrayList como un conjunto de celdas donde se guardan los
valores, exactamente igual que un array convencional. En la práctica será más fácil
trabajar con un ArrayList.
Trabajando con arrays es frecuente cometer errores al utilizar los índices; por ejemplo al
intentar guardar un elemento en una posición que no existe (índice fuera de rango).
Aunque las colecciones permiten el uso de índices, no es necesario indicarlos siempre.
Por ejemplo, en una colección del tipo ArrayList, cuando hay que añadir el elemento
"Amapola" , se puede hacer simplemente flores.add("Amapola") . Al no especificar índice, el
import java.util.ArrayList;
import java.util.List;
// Añadir elementos
listaNombres.add("Antonio");
listaNombres.add("Susana");
listaNombres.add("Sara");
listaNombres.add("Sara");
// Iterar la lista
for (String unNombre : listaNombres) {
System.out.println(unNombre);
}
System.out.println();
// Borrar elementos
listaNombres.remove(0);
listaNombres.remove("Sara");
// Sustituir elementos
listaNombres.set(0, "Pepe");
Tema 9 - Colecciones
// Otra forma de iterar la lista
for (int i = 0; i < listaNombres.size(); i++) {
System.out.println(listaNombres.get(i));
}
System.out.println();
}
}
Utilización de un Set
import java.util.HashSet;
import java.util.Set;
Tema 9 - Colecciones
// Añadir elementos
conjuntoTelefonos.add(600100100);
conjuntoTelefonos.add(600200200);
conjuntoTelefonos.add(600300300);
conjuntoTelefonos.add(600300300);
// Eliminar elementos
conjuntoTelefonos.remove(600200200);
// Iterar un conjunto
for (Integer unTelefono : conjuntoTelefonos) {
System.out.println(unTelefono);
}
System.out.println();
Tema 9 - Colecciones
▪ Map.of(clave1, valor1, clave2, valor2...): Método estático que permite crear un
diccionario inmutable con entre 0 y 10 entradas.
▪ get(clave): Devuelve el valor asociado a una clave.
▪ put(clave, valor): Permite insertar una pareja clave-valor.
▪ remove(clave): Elimina un elemento del conjunto.
▪ remove(clave, valor): Elimina un elemento del conjunto únicamente en caso de
que el valor asignado a la clave sea el indicado.
▪ containsKey(clave): Comprueba si una clave está presente en el diccionario.
▪ containsValue(valor): Comprueba si un valor está presente en el diccionario.
▪ isEmpty(): Verifica si el conjunto está vacío.
▪ clear(): Elimina todos los elementos del diccionario.
▪ size(): Devuelve el número de elementos de la lista.
▪ values(): Devuelve un Collection con los valores.
▪ keySet(): Devuelve un Set con todas las claves.
▪ entrySet(): Devuelve un Set con todos los pares (clave, valor).
Implementaciones de Map
Con cualquier Map podemos estructurar datos con forma de diccionario. Esta estructura
contiene una serie de elementos (que son las entradas) que a su vez están formadas por
un par (clave, valor). La clave (key) permite acceder al valor. No puede haber claves
duplicadas.
Utilización de un Map
import java.util.HashMap;
import java.util.Map;
// Iterar el diccionario
for (String clave : agendaTelefonica.keySet()) {
int valor;
Tema 9 - Colecciones
valor = agendaTelefonica.get(clave);
System.out.printf("El teléfono de %s es: %d\n", clave, valor);
}
System.out.println();
// Modificar entradas
agendaTelefonica.put("Antonio", 600777888);
3 Iteradores
Son clases auxiliares que implementan la interfaz Iterator. Nos permiten recorrer los
elementos de una colección. Con un iterador podemos recuperar fácilmente los elementos
y realizar operaciones con cada uno de ellos.
Tema 9 - Colecciones
como las de borrado están permitidas.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
// Imprimir la lista
System.out.println(listaNumeros);
}
}
Tema 9 - Colecciones
4 Localización e identificación de
objetos
4.1 Los métodos equals() y hashCode()
Son dos métodos que están presentes en la clase Object, de modo que cualquier otra
clase siempre va a disponer, al menos, de la implementación por defecto de los mismos.
Cuando trabajamos con colecciones, nos interesa que nuestras clases realicen su propia
Tema 9 - Colecciones
implementación de este métodos, dado que son necesarios para el correcto
funcionamiento de ciertas operaciones.
Para explicar estos métodos, tomaremos como ejemplo la siguiente clase Persona:
Método equals()
Permite conocer si un objeto es igual a otro. Es útil, por ejemplo, cuando buscamos
elementos en una lista o dentro de un conjunto.
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tema 9 - Colecciones
Persona other = (Persona) obj;
Método hashCode()
Este método devuelve un número entero que sirve como resumen o huella
característica de un objeto. Esto permite comparar o localizar objetos de forma rápida en
una estructura. El valor hash debe ser calculado en consistencia con la definición de
igualdad de la clase, por lo tanto, no podemos hablar de sobrescribir el método equals()
si no sobrescribimos también el método hashCode(). Esto se debe a que, cuando dos
objetos tienen el mismo hashCode(), es necesario utilizar equals() para comprobar si
se trata del mismo objeto.
@Override
public int hashCode() {
return Objects.hash(dni, edad, nombre);
}
import java.util.Objects;
Tema 9 - Colecciones
this.edad = edad;
}
@Override
public String toString() {
return "Persona [nombre=" + nombre + ", dni=" + dni + ", edad=" + edad + "]";
}
@Override
public int hashCode() {
return Objects.hash(dni, edad, nombre);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
import java.util.ArrayList;
import java.util.List;
// Iterar la lista
for (Persona unaPersona : listaPersonas) {
System.out.println(unaPersona);
}
}
}
Tema 9 - Colecciones
La fórmula utilizada para calcular hashCode() influirá en la eficiencia a la hora de
localizar objetos dentro de colecciones basadas en tablas Hash, como por ejemplo un
HashSet. Cuanto mejor sea la implementación de hashCode(), menos colisiones habrá
y, por lo tanto, mejor será la eficiencia de estas estructuras de datos.
5.1 Comparable
Tema 9 - Colecciones
Se trata de un interfaz sencillo:
5.2 Comparator
Se trata de un interfaz sencillo:
// ...
@Override
public int compareTo(Persona otraPersona) {
return this.nombre.compareTo(otraPersona.nombre);
}
}
Tema 9 - Colecciones
En este caso, hemos utilizado el nombre como criterio para implementar
compareTo(), por lo que la lista se ordenará alfabéticamente según el nombre de las
personas. Este será el criterio de ordenación por defecto. Por ejemplo:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// Ordenar la lista
Collections.sort(listaPersonas);
// Iterar la lista
for (Persona unaPersona : listaPersonas) {
System.out.println(unaPersona);
}
}
}
Producirá la salida:
Si queremos utilizar otros criterios diferentes para ordenar la lista, tendremos que
implementar la interfaz Comparator. Esto se suele hace creado una clase auxiliar que
será la que lo implemente. Aunque no es estrictamente necesario, suele ser buena idea
que esta clase esté anidada. Nuestra clase Persona, al completo, quedaría así:
import java.util.Comparator;
import java.util.Objects;
Tema 9 - Colecciones
Para ordenar por edad, haríamos lo siguiente:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// Ordenar la lista
Collections.sort(listaPersonas, new Persona.ComparadorEdad());
// Iterar la lista
for (Persona unaPersona : listaPersonas) {
System.out.println(unaPersona);
}
}
}
ejemplo:
Collections.sort(listaPersonas,
Collections.reverseOrder(new Persona.ComparadorEdad()));
Tema 9 - Colecciones
Cuadro de resumen sobre lo métodos relacionados con la búsqueda y la ordenación
Tema 9 - Colecciones
public static class ComparadorEdad implements Comparator<Persona> {
@Override
public int compare(Persona p1, Persona p2) {
return Integer.compare(p1.edad, p2.edad);
}
}
// Constructor
public Persona(String nombre, String dni, int edad) {
super();
this.nombre = nombre;
this.dni = dni;
this.edad = edad;
}
// toString
@Override
public String toString() {
return "Persona [nombre=" + nombre + ", dni=" + dni + ", edad=" + edad + "]";
}
// hashCode
@Override
public int hashCode() {
return Objects.hash(dni, edad, nombre);
}
// equals
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
7 Referencias
▪ Colecciones explicadas con animaciones
▪ Collections Framework Overview
▪ Tutorial: Collections
Tema 9 - Colecciones