Colecciones en Programación
Colecciones en Programación
Programación
UF7. Clases de uso general
7.2. Colecciones
7.2. Colecciones MP_0485. Programación
UF7. Clases de uso general
Índice
Objetivos 3
Introducción 4
Interfaz Set 36
Los mapas 46
HashMap 48
TreeMap 53
Amplía conocimientos 58
Resumen 60
Objetivos
¡Ánimo y adelante!
3
Introducción
Para resolver este problema podemos utilizar otro recurso que nos
ofrece Java;
las clases que representan colecciones
y las clases que representan mapas.
Java cuenta con muchísimas clases que sirven para representar colecciones de datos y mapas,
clases que forman parte de una compleja jerarquía. En la siguiente imagen te mostramos la
jerarquía que corresponde a las clases con las que vamos a trabajar:
4
En la imagen hemos simpli cado el árbol jerárquico de las colecciones y mapas, eliminando
algunas ramas y dejando lo más relevante. Lo importante es que comprendas que cuando
utilizas una clase, esta no está aislada, sino que forma parte de una estructura jerárquica más
compleja.
Ten en cuenta que en la imagen los rectángulos con fondo negro son interfaces y los
rectángulos con fondo blanco son clases. Así puedes fácilmente deducir que la clase
Vector implementa la interfaz List, e indirectamente, también las interfaces Collection e Iterable.
También puedes comprobar que la clase LinkedList implementa las interfaces List y Queue, y
también, de manera indirecta, las interfaces Collection e Iterable.
Observa además que las clases que implementan la interfaz Map no implementan las
interfaces Collection e Iterable, es decir, están separadas y no forman parte de la misma
estructura o familia jerárquica. Aunque esto es relativo, porque ya sabes que todas las clases
heredan de la superclase Object, así que de alguna manera cualquier clase está unida a otra a
través de la superclase Object.
Tanto colecciones como mapas representan un conjunto o colección de elementos, pero hay
una importante diferencia entre ambos:
1 Los mapas no implementan las interfaces Iterable y Collection, tal como puedes
apreciar en la imagen. En el siguiente apartado aprenderás qué aportan estas
interfaces a las clases que las implementan.
5
La clave de cada elemento del mapa es como el carnet de
identidad de una persona. Es única para cada elemento y
sirve para identi carlo inequívocamente.
Tipos de colecciones
Observando de nuevo la imagen, puedes apreciar que hay colecciones que implementan la
interfaz List (Vector, ArrayList y LinkedList) y colecciones que implementan la interfaz
Set (HashSet, TreeSet). Clasi caremos las colecciones según se trate de listas o conjuntos.
1 Listas: objetos que pertenecen a una clase que implementa la interfaz List. Los
elementos que se añaden a una lista estarán siempre situados según el orden en
que se han añadido. Se admiten valores duplicados.
2 Conjuntos: objetos que pertenecen a una clase que implementa la interfaz Set,
inspirada en la teoría de conjuntos de matemáticas. Los elementos que se añaden
no guardan ningún orden especí co, ni se sitúan en el orden en que se van
introduciendo. La característica más importante de los conjuntos es que no
admiten valores duplicados.
6
Colecciones y mapas: estructura
Todas las clases e interfaces que se encuentran en este esquema pertenecen al paquete
java.util, a excepción de la interfaz Iterable, que se encuentra en java.lang.
1
2
3
7 8
4 5 6
9
10 11 12 13 14
15
1 Iterable
Las clases que implementan la interfaz Iterable tienen la característica de que sus elementos pueden
recorrerse con una estructura de tipo foreach y además poseen el método iterator().
Trabajaremos con estos conceptos más adelante.
2 Map
Los mapas asocian una clave a cada elemento, que servirá después como llave para acceder a dicho
elemento.
3 Collection
Las clases que implementan la interfaz Collection representan una colección dinámica de objetos.
Internamente trabajan con arrays.
4 List
Interfaz que implementa las colecciones de datos que se organizan en forma de lista, es decir, un
elemento detrás de otro en el orden en que se van introduciendo.
7
5 Queue
Las clases que implementan la interfaz Queue representan colecciones cuyos elementos se organizan en
forma de cola.
Una cola es una estructura de datos en memoria RAM donde el primer elemento que entró, es el
primero en salir. También se denominan estructuras FIFO, abreviatura del inglés “First In, First Out”.
6 Set
Conjuntos: los elementos que se añaden no se sitúan en el orden natural en que se van introduciendo.
La característica más importante de los conjuntos es que no admiten valores duplicados.
7 HashMap
Colección de elementos donde cada elemento tiene asociada una clave. Los elementos no se sitúan en el
orden en que se van añadiendo.
8 SortedMap
Interfaces cuyas clases que la implementan representan mapas cuyos elementos quedan ordenados
según la clave.
9 TreeMap
Colección de elementos donde cada elemento tiene asociada una clave. Los elementos se sitúan en orden
de clave.
10 Vector
Lista basada en un array tipo vector con una longitud inicial de 10 elementos, que irá creciendo en 10
elementos más cuando sea necesario, permitiendo que crezca dinámicamente. Sus métodos son
sincronizados, es decir, es útil cuando trabajamos con programas multitarea utilizando distintos hilos
de ejecución.
11 ArrayList
Lista basada en un array tipo vector con una longitud inicial de 10 elementos, que irá creciendo en 10
elementos más cuando sea necesario, permitiendo que crezca dinámicamente. A diferencia de la clase
Vector, sus métodos no son sincronizados.
12 LinkedList
Basado en una lista enlazada y con los elementos organizados en forma de cola. Mucho más e ciente
que Vector y ArrayList cuando hay que hacer muchas inserciones y eliminaciones.
8
13 HashSet
Colección de objetos que no guardan ningún tipo de orden. Los elementos no se sitúan en el mismo
orden que se han ido introduciendo. Puesto que se trata de un conjunto, no admite duplicados, es
decir, no admite ningún nuevo elemento que, aplicándole el método equals sobre cualquiera de los
existentes, dé true.
14 SortedSet
Las clases que implementan esta interfaz representan colecciones de objetos que pueden ordenarse en
función del método compareTo de cada elemento con el resto de los elementos, y además no admiten
duplicados, puesto que se trata de conjuntos.
15 TreeSet
Colección de objetos que pueden ordenarse en función del método compareTo de cada elemento con el
resto de los elementos y además, no admiten duplicados, es decir, no admite ningún nuevo elemento
que, aplicándole el método equals sobre cualquiera de los existentes, dé true.
9
Las clasees Vector y ArrayList
Las clases ArrayList y Vector son muy similares, ya que ambas basan su estructura de datos
10
Vector()
Vector(int initialCapacity)
Vector(int initialCapacity, int capacityIncrement)
ArrayList()
ArrayList(int initialCapacity)
La diferencia entre Vector y ArrayList es que un vector tiene propiedad de sincronización, algo
útil para la concurrencia, es decir, cuando realizamos programas multitarea utilizando
distintos hilos de ejecución, ya que garantiza mayor seguridad.
ArrayList no es sincronizado, por lo que resulta más e ciente si no estamos trabajando con
hilos y métodos sincronizados. Los ejemplos que veremos a lo largo de este apartado estarán
basados en la clase ArrayList, pero podrías aplicarlos igual con un objeto de la clase Vector.
ArrayList y Vector son clases genéricas; luego hay que especi car la clase a la que pertenecen
los elementos que portará la lista. Veamos varios ejemplos:
Lista inicial de 10 elementos de tipo String. En el momento que se añada el elemento 11, se añadirán
Lista inicial de 15 elementos de tipo Triangulo. En el momento en que se añada el elemento 16, se
11
ArrayList<Triangulo> trians = new ArrayList<Triangulo>(15);
Lista inicial de 15 elementos de tipo Triángulo. En el momento en que se añada el elemento 16, se
duplicará el array. En este caso, los métodos del objeto trians no son sincronizados.
Recuerda: las clases genéricas tienen la posibilidad de declarar una o varias propiedades,
cuyo tipo puede variar. El tipo de dicha propiedad será establecido en el momento de crear
un objeto mediante la notación <tipo>. Si lo necesitas, puedes volver a revisar la
documentación sobre Clases genéricas en la unidad anterior.
Ejemplo practico
Crearemos una lista de objetos Triangulo. Para ello, comienza por crear un nuevo proyecto en
Eclipse y añade la clase Triangulo con el siguiente código:
12
public class Triangulo {
private int lado1;
private int lado2;
private int lado3;
@Override
public String toString() {
return "Triangulo [" + lado1 + ", " + lado2 + ", " + lado3
}
OJO: igual que ocurría con los arrays, en las colecciones el primer
elemento ocupa la posición 0.
13
import java.util.ArrayList;
Nuestro objeto ArrayList tiene tres elementos que están situados en las posiciones 0, 1 y 2.
Algunos de los métodos que verás a continuación son comunes a todas las clases que
implementan la interfaz Collection y otros son propios exclusivamente de la interfaz List.
14
int size()
El método size devuelve el número de elementos que contiene la lista. En el ejemplo anterior
estamos utilizando este método para controlar cuándo deber terminar el bucle for.
Añade el elemento pasado como argumento al nal de la lista. Devuelve true si el elemento se
ha añadido con éxito y devuelve false si, por alguna circunstancia, el elemento no se ha podido
añadir.
void clear()
boolean contains(Object o)
Devuelve true si la colección contiene el objeto especi cado como argumento, de lo contrario
devuelve false.
boolean isEmpty()
15
Object get(int pos)
El método get devuelve el elemento que ocupa la posición especi cada como argumento. En el
anterior ejemplo lo estamos utilizando para recuperar el objeto Triangulo que ocupa la posición
1 y después para recorrer cada uno de los objetos Triangulo por medio de un bucle for.
También es posible añadir un nuevo elemento en la posición deseada. Puedes probar a añadir el
siguiente código al ejemplo:
Elimina el elemento que ocupa la posición especi cada en el argumento y retorna el elemento
borrado. Puedes ponerlo en práctica añadiendo este código:
Triangulo t = trians.remove(2);
System.out.println("Se ha eliminado el triángulo: " + t.verTipo());
Se ha eliminado el elemento que ocupaba la posición indicada y los elementos que le seguían
han tenido que desplazarse una posición hacia arriba.
16
Anteriormente ya has iterado de la siguiente forma:
La estructura for each (para cada) es un formato especial de la instrucción for que permite
repetir un grupo de sentencias tantas veces como elementos tenga una colección o un array.
El formato es el siguiente:
Por ejemplo, para recorrer los elementos de una colección de objetos Triangulo:
17
import java.util.ArrayList;
El bucle se ejecuta tantas veces como elementos tenga la colección trians. En cada iteración
El método iterator() retorna un objeto de la clase Iterator que provee de un mecanismo para
recorrer secuencialmente los elementos de una colección a través de un cursor. Llamamos
cursor a una marca que se va desplazando y que va posicionándose en el siguiente elemento a
leer dentro de la colección.
18
import java.util.ArrayList;
import java.util.Iterator;
Primero, hemos tenido que obtener el objeto Iterator asociado a la colección trians.
Nuestro nuevo objeto Iterator se llama itera y estamos utilizándolo para acceder
secuencialmente a los elementos de la colección trians, de manera similar a como se recorren
los registros de un chero secuencial. Para lograrlo utilizamos los siguiente métodos de la
clase Iterator:
19
boolean hasNext()
Esta función devuelve true mientras sigan quedando elementos para iterar en la colección. En el
ejemplo lo estamos utilizando para ejecutar el bucle mientras haya más elementos que
recorrer.
Object next()
CadaTriangulo = itera.next();
Esta sentencia devuelve el objeto Triangulo que corresponde a la posición donde se encuentra
el cursor de lectura.
while (itera.hasNext()) {
cadaTriangulo = itera.next();
System.out.println(cadaTriangulo.verTipo());
}
Recuerda: podrás iterar con cualquier tipo de colección que derive de la interfaz Iterator.
20
Pero, ¿cuándo utilizo la estructura for ... each
y cuándo un objeto Iterator?
La estructura for ... each parece más sencilla cuando solo quiero recorrer los
elementos, por ejemplo, para realizar un listado. Pero si lo que deseamos es eliminar de la
colección los elementos que cumplan una determinada condición, entonces podemos
21
import java.util.ArrayList;
nombres.add("Carmen");
nombres.add("Rosa");
nombres.add("Carmen");
nombres.add("Miguel");
nombres.add("Carmen");
iterando.
22
El método Iterator.remove()
Los objetos de la clase Iterator poseen el método remove(), que elimina el objeto que acaba de
iterarse. Esto vendría a resolver el problema del ejemplo anterior.
import java.util.ArrayList;
import java.util.Iterator;
nombres.add("Carmen");
nombres.add("Rosa");
nombres.add("Carmen");
nombres.add("Miguel");
nombres.add("Carmen");
}
}
}
23
Para saber más ...
ACCEDER
ACCEDER
24
LinkedList: listas enlazadas
que también deriva de List, Collection e Iterable. Todos los ejemplos que has realizado en el
¿Exactamente igual?
Parece que funciona igual pero, en realidad, una colección LinkedList es diferente a una
colección ArrayList. Los elementos están organizados en memoria RAM de una manera muy
distinta. Descubriremos las ventajas de uno sobre otro.
Puedes poner en práctica estos dos ejemplos que ya utilizamos anteriormente, pero ahora con
objetos LinkedList.
25
import java.util.LinkedList;
nombres.add("Carmen");
nombres.add("Rosa");
nombres.add("Carmen");
nombres.add("Miguel");
nombres.add("Carmen");
import java.util.LinkedList;
26
Hay dos características muy importantes que hacen de LinkedList un
tipo de colección muy especial:
2 Un objeto de tipo LinkedList es una cola, característica que está obligada a cumplir
al implementar la interfaz Queue.
Las listas enlazadas son estructuras de datos organizadas como conjuntos de registros en
memoria RAM, donde cada registro está situado a partir de una determinada dirección de
memoria y enlazado con el siguiente.
Cada registro contendrá, además de los datos necesarios (en el ejemplo de la imagen: nombre,
teléfono y dirección), la dirección de memoria del siguiente registro, excepto en el último
registro, que contendrá null.
Direcciones de memoria
Lista enlazada.
Existen listas enlazadas y listas doblemente enlazadas, donde cada elemento contiene la
dirección de memoria, no solo del siguiente elemento, sino también del anterior. Los objetos
LinkedList representan listas doblemente enlazadas.
27
¿Cuándo usar LinkedList y cuándo ArrayList?
El hecho de que los objetos LinkedList sean listas enlazadas resulta, en ocasiones, una ventaja
y en otras, una desventaja.
Es más e ciente usar LinkedList cuando tenemos una lista grande en la que hay
que realizar muchas inserciones en cualquier posición.
Es más e ciente usar LinkedList cuando tenemos una lista grande, donde hay que
realizar muchas eliminaciones en cualquier posición.
3 Cuando un objeto ArrayList necesita crecer porque se ha llegado al nal del array,
el sistema debe crear un nuevo array más grande y copiar todos los elementos del
array antiguo. Esto no ocurre con un objeto LinkedList, ya que no está basado en
arrays. Si vamos a utilizar un ArrayList y sabemos de antemano que crecerá mucho,
puede ser interesante crearlo de antemano de un tamaño grande.
Los objetos LinkedList pueden crecer todo lo que sea necesario sin peligrar el
rendimiento.
28
4 Si necesitamos acceder aleatoriamente a cualquier elemento, con ArrayList se
accede directamente al elemento buscado sin tener que recorrer todos los
elementos (acceso aleatorio).
buscado = lista.get(3);
También se denominan estructuras FIFO, abreviatura del inglés “First In, First Out”.
29
30
Para demostrar la forma en la que los objetos de tipo LinkedList se sitúan formando una
estructura FIFO o cola puedes ejecutar este programa:
import java.util.LinkedList;
import java.util.Queue;
sentencia "String sms = mensajes.remove();" saca un elemento de la salida de la cola, pero antes lo
Con la sentencia
Queue<String> mensajes = new LinkedList<String>();
31
LinkedList<String> mensajes = new LinkedList<String>();
32
33
34
3
Interfaz Set
3
Interfaz Set
Trabajaremos con las clases HashSet y TreeSet. Observa que derivan de Collection e Iterable,
por lo que puedes deducir que puedes iterar con una estructura for ... each, usar el método
iterator() y otros métodos como add(), remove(), clear(), etc. Sin embargo, no podrás utilizar el
1 Ambas clases no admiten elementos duplicados, ya que son conjuntos. Para evitar
los duplicados, por cada nuevo elemento que se va a almacenar se comprueba que
la función equals() sobre el nuevo elemento comparado con todos los anteriores
devuelva siempre el valor false. Recuerda que dos objetos se consideran iguales
cuando tienen el mismo hashCode. Si crees que lo necesitas, repasa de nuevo el
apartado sobre los métodos hashcode() y equals() de la unidad anterior.
3
Conjuntos sin orden: HashSet
import java.util.HashSet;
import java.util.Set;
nombres.add("Rosa");
nombres.add("Carlos");
nombres.add("Miguel");
nombres.add("Carlos"); // Este no se añadirá.
nombres.add("Sole");
nombres.add("Adrián");
nombres.add("Angel");
nombres.add("Amelia");
nombres.add("Fernando");
nombres.add("Sebas");
nombres.add("Lucas");
3
Con las cadenas de texto está muy claro cuándo hay un duplicado, pero, ¿qué pasa con
objetos Triangulo? ¿Cuándo se considera que dos objetos son
iguales? La respuesta está en los métodos hashCode() y equals().
3
public class Triangulo {
private int lado1;
private int lado2;
private int lado3;
@Override
public String toString() {
return "Triangulo [" + lado1 + ", " + lado2 + ", " + lado3
}
@Override
public int hashCode() {
return this.lado1+this.lado2+this.lado3;
}
@Override
public boolean equals(Object obj) {
if (this.hashCode() == obj.hashCode())
return true;
else
return false;
}
}
Ahora podemos crear una colección de objetos Triangulo donde no puedan existir dos
triángulos cuya suma de los lados sea igual.
import java.util.HashSet;
La clase TreeSet funciona igual que la clase HashSet, pero en este caso
los elementos se ordenan según el criterio que dicte el método
compareTo(). Además, la clase TreeSet implementa la interfaz SortedSet.
Puedes probar el mismo ejemplo de los nombres del apartado anterior, pero ahora con un
objeto TreeSet.
import java.util.Set;
import java.util.TreeSet;
nombres.add("Rosa");
nombres.add("Carlos");
nombres.add("Miguel");
nombres.add("Carlos"); // Este no se añadirá.
nombres.add("Sole");
nombres.add("Adrián");
nombres.add("Angel");
nombres.add("Amelia");
nombres.add("Fernando");
nombres.add("Sebas");
nombres.add("Lucas");
Comprueba que, igual que antes, no admite duplicados, pero ahora además los
Está muy claro cómo deberían ordenarse un grupo de objetos String (alfabéticamente) o un
grupo de objetos Integer (numéricamente de menor a mayor). Sin embargo, no resulta tan
claro cómo deben ordenarse los objetos. En este caso, es el programador que desarrolla la
clase quien debe decidir qué tipo de ordenación resulta más interesante, y para ello debe
implementar el método compareTo() de la interfaz Comparable.
Para implementar el método compareTo() hay que respetar las siguientes reglas:
Vamos a implementar el método compareTo() en la clase Triangulo para que los triángulos se
ordenen según la suma de sus lados. Para lograrlo, la clase Triangulo deberá implementar la
interfaz Comparable, así que lo primero que haremos será modi car la cabecera de la clase de
la siguiente manera:
<Triangulo>.
4
La nueva clase Triangulo quedará así:
this.lado1 = lado1;
this.lado2 = lado2;
this.lado3 = lado3;
}
@Override
public String toString() {
return "Triangulo [" + lado1 + ", " + lado2 + ", " + lado3
}
@Override
public int hashCode() {
return this.lado1+this.lado2+this.lado3;
}
@Override
public boolean equals(Object obj) {
if (this.hashCode() == obj.hashCode())
return true;
else
return false;
}
@Override
public int compareTo(Triangulo o) {
if (this.hashCode()==o.hashCode())
return 0;
else if (this.hashCode()>o.hashCode())
return 1;
else
return -1;
}
}
4
Ahora puedes crear una colección de objetos Triangulo exactamente igual que lo hiciste con un
HashSet, pero ahora con un objeto TreeSet.
import java.util.TreeSet;
Si has ejecutado ya, habrás comprobado que sigue sin admitir duplicado. El
lados.
4
Los mapas
Los mapas son un tipo especial de colecciones que asocian una clave a
cada elemento, que servirá después como llave para acceder a dicho
elemento. Las clases que representan mapas derivan de la interfaz Map.
En este apartado trabajaremos con las clases HashMap y TreeMap. Es importante tener en
cuenta que estas clases no derivan de Iterable y Collection, por lo que muchos de los
4
Los mapas tienen ciertas similitudes con los conjuntos:
1 Igual que ocurre con los conjuntos, los elementos no se colocan en el orden en
que se han añadido.
Vamos a comenzar por crear una colección de nombres, es decir, de objetos String.
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
// Recorrer el mapa
Set<String> claves = nombres.keySet();
4
Vamos a analizar el ejemplo paso a paso:
HashMap es una clase genérica, maneja internamente dos objetos cuyo tipo
2 Añadir elementos
Luego hemos añadido varios elementos utilizando el método put, que tiene el siguiente
formato:
4
objMap.put(Object key, Object element);
propósito para que puedas comprobar lo que ocurre en este caso. El método
El método get() permite el acceso aleatorio a través de la clave. Si la clave buscada no existe
devuelve null.
// Acceder a un elemento por la clave
System.out.println(nombres.get("31234443H"));
4 Iterando
Las colecciones de tipo Map no descienden de Iterable, luego es fácil deducir que no son
colecciones iterables. No podemos hacer ninguna de estas operaciones con nuestro objeto
nombres:
Sin embargo, hay una solución. Las clases que implementan la interfaz Map tienen un par de
métodos que vienen a salvarnos de esta situación:
objMap.values(); // Devuelve un objeto de tipo Set con los valores, sin las claves.
Los objetos devueltos por estos métodos sí son iterables, ya que son conjuntos.
// Recorrer el mapa
Set<String> claves = nombres.keySet();
contiene el mapa.
5
TreeMap
Como prueba, vamos a utilizar el mismo ejemplo anterior, pero ahora con un objeto TreeMap.
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
// Recorrer el mapa
Set<String> claves = nombres.keySet();
por DNI.
5
Aprovecharemos la clase Triangulo creada anteriormente para crear un
objeto TreeMap, que contendrá una colección de objetos Triangulo.
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
}
}
5
Ejemplo práctico: cuenta bancaria
import java.time.LocalDate;
@Override
public String toString() {
return fecha + " Concepto=" + concepto + ", Cantidad=" + ca
}
}
5
import java.util.ArrayList;
@Override
public String toString() {
return "Numero=" + numeroCuenta + ", Cliente=" + cliente +
}
Ahora tendrás que crear una clase Principal que te permita poner en práctica el funcionamiento
de un objeto CuentaBancaria.
5
public class Principal {
public static void main(String args[]) {
CuentaBancaria miCuenta = new CuentaBancaria(38143, "Amelia
miCuenta.agregarMovimiento("Ingreso Nómina", 2000);
miCuenta.agregarMovimiento("Pago luz", -120);
miCuenta.agregarMovimiento("Compra supermercado", -27);
miCuenta.agregarMovimiento("Ingreso Efectivo", 8000);
System.out.println(miCuenta.toString());
System.out.println(miCuenta.listarMovimientos());
}
}
5
Amplía conocimientos
En esta lección hemos visto buena parte de las clases Java que puedes
utilizar para construir colecciones de objetos, pero aún hay más.
Ahora te invitamos, de manera opcional, a que investigues por tu cuenta y utilices algún tipo
más de colección de las que no hemos visto en esta unidad. Por ejemplo, puedes probar con:
Stack: lista de elementos basada en un array de tipo pila o estructura LIFO (Last In,
First Out), donde el último en entrar es el primero en salir.
¿Te animas?
5
¿Cómo elegir el tipo de colección?
La siguiente imagen resume de manera esquemática cómo decidir el tipo de colección que
necesitamos:
5
Resumen
Has terminado la lección, vamos a ver los puntos más importantes que
hemos tratado.
Java cuenta con una gran cantidad de clases que representan colecciones de datos.
Todas ellas pertenecen al paquete java.util.
Todas ellas forman parte de una jerarquía. En la siguiente imagen puedes ver la
jerarquía de las colecciones más utilizadas por la comunidad de programadores
Java.