Java Collections Framework es una de las partes fundamentales del lenguaje de programación Java. Las colecciones se utilizan en casi todos los lenguajes de programación. La mayoría de los lenguajes de programación admiten varios tipos de colecciones, como List , Set , Queue , Stack , etc.
¿Qué es Java Collections Framework?
Las colecciones son como contenedores que agrupan varios elementos en una sola unidad. Por ejemplo, un tarro de chocolates, una lista de nombres, etc.
Las colecciones se utilizan en todos los lenguajes de programación y cuando llegó Java, también vino con algunas clases de colección: Vector , Stack , Hashtable , Array .
Java 1.2 proporcionó el marco de colecciones , que es la arquitectura para representar y manipular colecciones en Java de forma estándar. El marco de colecciones de Java consta de las siguientes partes:
1. Interfaces
Las interfaces de Java Collections Framework proporcionan el tipo de datos abstracto para representar la colección.
java.util.Collection es la interfaz raíz de Collections Framework. Se encuentra en la parte superior de la jerarquía de Collections Framework. Contiene algunos métodos importantes como size(), iterator(), add(), remove(), clear() que cada clase Collection debe implementar.
Otras interfaces importantes son java.util.List, java.util.Set, java.util.Queue y java.util.Map. Map es la única interfaz que no hereda de la interfaz Collection, pero es parte del marco de trabajo Collections. Todas las interfaces del marco de trabajo Collections están presentes en el paquete java.util.
2. Clases de implementación
El marco de trabajo de colecciones de Java proporciona clases de implementación para las interfaces de colección principales. Podemos usarlas para crear distintos tipos de colecciones en el programa Java.
Algunas clases de colección importantes son ArrayList, LinkedList, HashMap, TreeMap, HashSet y TreeSet. Estas clases resuelven la mayoría de nuestras necesidades de programación, pero si necesitamos alguna clase de colección especial, podemos extenderlas para crear nuestra clase de colección personalizada.
Java 1.5 presentó clases de colección seguras para subprocesos que nos permitieron modificar colecciones mientras iterábamos sobre ellas. Algunas de ellas son CopyOnWriteArrayList, ConcurrentHashMap y CopyOnWriteArraySet. Estas clases se encuentran en el paquete java.util.concurrent.
Todas las clases de colección están presentes en los paquetes java.util y java.util.concurrent.
3. Algoritmos
Los algoritmos son métodos útiles para proporcionar algunas funcionalidades comunes como búsqueda, clasificación y mezcla.
Diagrama de clases del marco de colecciones
El diagrama de clases que se muestra a continuación muestra la jerarquía de Collections Framework. Para simplificar, he incluido solo las interfaces y clases que se usan con más frecuencia.
Beneficios del marco de colecciones de Java
El marco de colecciones de Java tiene los siguientes beneficios:
- Esfuerzo de desarrollo reducido : incluye casi todos los tipos comunes de colecciones y métodos útiles para iterar y manipular los datos. De esta manera, podemos concentrarnos más en la lógica empresarial en lugar de diseñar nuestras API de colección.
- Mejor calidad : el uso de clases de recopilación de núcleos que estén bien probadas aumenta la calidad de nuestro programa en lugar de utilizar cualquier estructura de datos desarrollada en casa.
- Reutilización e interoperabilidad
- Reduce el esfuerzo de mantenimiento porque todo el mundo conoce las clases de la API de colección.
Interfaces API de colecciones de Java
Las interfaces de colección de Java son la base del marco de colecciones de Java. Tenga en cuenta que todas las interfaces de colección principales son genéricas; por ejemplo, la interfaz pública CollectionE. La sintaxis E es para genéricos y cuando declaramos Collection, debemos usarla para especificar el tipo de objeto que puede contener. Ayuda a reducir los errores de tiempo de ejecución al verificar el tipo de los objetos en tiempo de compilación.
Para mantener manejable la cantidad de interfaces de recopilación de núcleos, la plataforma Java no proporciona interfaces independientes para cada variante de cada tipo de recopilación. Si se invoca una operación no compatible, una implementación de recopilación genera una UnsupportedOperationException.
1. Interfaz de colección
Esta es la raíz de la jerarquía de colecciones. Una colección representa un grupo de objetos conocidos como sus elementos. La plataforma Java no proporciona ninguna implementación directa de esta interfaz.
La interfaz tiene métodos para indicarle cuántos elementos hay en la colección (tamaño, está vacío), para verificar si un objeto dado está en la colección (contiene), para agregar y eliminar un elemento de la colección (agregar, eliminar) y para proporcionar un iterador sobre la colección (iterador).
La interfaz de colección también proporciona métodos de operaciones masivas que funcionan en toda la colección: containsAll, addAll, removeAll, maintainAll, clear.
Los métodos toArray se proporcionan como un puente entre las colecciones y las API más antiguas que esperan matrices en la entrada.
2. Interfaz del iterador
La interfaz Iterator proporciona métodos para iterar sobre los elementos de la colección. Podemos obtener la instancia de iterator usando iterator()el método. Iterator reemplaza a Enumerationen el marco de colecciones de Java. Los iteradores permiten al llamador eliminar elementos de la colección subyacente durante la iteración. Los iteradores en las clases de colección implementan el patrón de diseño Iterator .
3. Establecer interfaz
Un conjunto es una colección que no puede contener elementos duplicados. Esta interfaz modela la abstracción matemática de conjuntos y se utiliza para representar conjuntos, como la baraja de cartas.
La plataforma Java contiene tres implementaciones de conjuntos de propósito general: HashSet, TreeSety LinkedHashSet. La interfaz de conjunto no permite el acceso aleatorio a un elemento de la colección. Puede utilizar un iterador o un bucle foreach para recorrer los elementos de un conjunto.
4. Interfaz de lista
List es una colección ordenada y puede contener elementos duplicados. Puedes acceder a cualquier elemento desde su índice. List es más como una matriz con longitud dinámica. List es uno de los tipos de colección más utilizados ArrayListy LinkedListson clases de implementación de la interfaz List.
La interfaz de lista proporciona métodos útiles para agregar un elemento en un índice específico, eliminar/reemplazar elementos según el índice y obtener una sublista utilizando el índice.
List strList = new ArrayList();//add at laststrList.add(0, "0");//add at specified indexstrList.add(1, "1");//replacestrList.set(1, "2");//removestrList.remove("1");
La clase Colecciones proporciona algunos algoritmos útiles para listas: ordenar, mezclar, invertir, búsqueda binaria, etc.
5. Interfaz de cola
Una cola es una colección que se utiliza para almacenar varios elementos antes de su procesamiento. Además de las operaciones básicas de la colección, una cola proporciona operaciones adicionales de inserción, extracción e inspección.
Las colas suelen ordenar los elementos de forma FIFO (primero en entrar, primero en salir), aunque no necesariamente. Entre las excepciones se encuentran las colas de prioridad, que ordenan los elementos según un comparador suministrado o según el orden natural de los elementos. Cualquiera sea el orden que se utilice, el elemento que se eliminaría mediante una llamada a remove o poll es el que encabeza la cola. En una cola FIFO, todos los elementos nuevos se insertan al final de la cola.
6. Interfaz de desencolado
Una colección lineal que admite la inserción y eliminación de elementos en ambos extremos. El nombre deque es la abreviatura de “double-ended queue” (cola de doble extremo) y normalmente se pronuncia “deck” (baraja). La mayoría de las implementaciones de Deque no imponen límites fijos en la cantidad de elementos que pueden contener, pero esta interfaz admite deques con capacidad restringida, así como aquellos que no tienen un límite de tamaño fijo.
Esta interfaz define métodos para acceder a los elementos en ambos extremos de la cola doble. Se proporcionan métodos para insertar, eliminar y examinar el elemento.
7. Interfaz del mapa
Java Map es un objeto que asigna claves a valores. Un mapa no puede contener claves duplicadas: cada clave puede asignarse a un valor como máximo.
La plataforma Java contiene tres implementaciones de mapas de propósito general: HashMap, TreeMap y LinkedHashMap.
Las operaciones básicas de Map son put, get, containsKey, containsValue, size y isEmpty.
8. Interfaz ListIterator
Un iterador para listas que permite al programador recorrer la lista en cualquier dirección, modificar la lista durante la iteración y obtener la posición actual del iterador en la lista.
Java ListIterator no tiene ningún elemento actual; su posición del cursor siempre se encuentra entre el elemento que devolvería una llamada a previous() y el elemento que devolvería una llamada a next().
9. Interfaz SortedSet
SortedSet es un conjunto que mantiene sus elementos en orden ascendente. Se proporcionan varias operaciones adicionales para aprovechar el ordenamiento. Los conjuntos ordenados se utilizan para conjuntos ordenados naturalmente, como listas de palabras y listas de miembros.
10. Interfaz de SortedMap
Un mapa que mantiene sus asignaciones en orden ascendente de clave. Este es el análogo de Map de SortedSet. Los mapas ordenados se utilizan para colecciones de pares clave/valor ordenados naturalmente, como diccionarios y directorios telefónicos.
Clases de colecciones de Java
El marco de trabajo de colecciones de Java viene con muchas clases de implementación para las interfaces. Las implementaciones más comunes son ArrayList , HashMap y HashSet. Java 1.5 incluía implementaciones concurrentes; por ejemplo, ConcurrentHashMap y CopyOnWriteArrayList. Por lo general, las clases de colección no son seguras para subprocesos y su iterador es a prueba de errores. En esta sección, aprenderemos sobre las clases de colección que se usan comúnmente.
1. Clase HashSet
Java HashSet es la implementación básica de la interfaz Set que está respaldada por un HashMap . No ofrece garantías sobre el orden de iteración del conjunto y permite el elemento nulo .
Esta clase ofrece un rendimiento de tiempo constante para las operaciones básicas ( add, remove, containsy size), suponiendo que la función hash distribuye los elementos correctamente entre los contenedores. Podemos establecer la capacidad inicial y el factor de carga para esta colección. El factor de carga es una medida de qué tan lleno se le permite al mapa hash llegar antes de que su capacidad se incremente automáticamente.
2. Clase TreeSet
Una NavigableSetimplementación basada en un TreeMap. Los elementos se ordenan utilizando su orden natural o mediante un Comparatorproporcionado en el momento de creación del conjunto, según el constructor que se utilice.
Consulte: Comparador comparable de Java
Esta implementación proporciona un costo de tiempo log(n) garantizado para las operaciones básicas (agregar, eliminar y contener).
Tenga en cuenta que el orden mantenido por un conjunto (ya sea que se proporcione o no un comparador explícito) debe ser consistente con equals si se va a implementar correctamente la interfaz Set. (Consulte Comparable o Comparator para obtener una definición precisa de consistente con equals). Esto es así porque la interfaz Set se define en términos de la operación equals, pero una instancia TreeSet realiza todas las comparaciones de elementos utilizando su método compareTo (o compare), por lo que dos elementos que se consideran iguales por este método son, desde el punto de vista del conjunto, iguales.
3. Clase ArrayList
ArrayList de Java es la implementación de la interfaz List en forma de matriz redimensionable. Implementa todas las operaciones de lista opcionales y permite todos los elementos, incluido el valor nulo. Además de implementar la interfaz List, esta clase proporciona métodos para manipular el tamaño de la matriz que se utiliza internamente para almacenar la lista. (Esta clase es aproximadamente equivalente a Vector, excepto que no está sincronizada).
Las operaciones de tamaño, isEmpty, get, set, iterator y list iterator se ejecutan en tiempo constante. La operación add se ejecuta en tiempo constante amortizado, es decir, agregar n elementos requiere tiempo O(n). Todas las demás operaciones se ejecutan en tiempo lineal (a grandes rasgos). El factor constante es bajo en comparación con el de la implementación de LinkedList.
Lectura adicional: Java ArrayList y Iterator
4. Clase LinkedList
Implementación de listas doblemente enlazadas de las interfaces List y Deque. Implementa todas las operaciones de listas opcionales y permite todos los elementos (incluidos los valores nulos).
Todas las operaciones se realizan como se espera para una lista doblemente enlazada. Las operaciones que indexan la lista recorrerán la lista desde el principio o el final, lo que esté más cerca del índice especificado.
5. Clase HashMap
Implementación de la interfaz Map basada en tabla hash. Esta implementación proporciona todas las operaciones de mapa opcionales y permite valores nulos y la clave nula. La clase HashMap es aproximadamente equivalente a Hashtable, excepto que no está sincronizada y permite valores nulos. Esta clase no garantiza el orden del mapa.
Esta implementación proporciona un rendimiento de tiempo constante para las operaciones básicas ( gety put). Proporciona constructores para establecer la capacidad inicial y el factor de carga para la colección.
Más información: HashMap vs ConcurrentHashMap
6. Clase TreeMap
Implementación de NavigableMap basada en un árbol rojo-negro. El mapa se ordena según el orden natural de sus claves o mediante un comparador proporcionado en el momento de la creación del mapa, según el constructor que se utilice.
Esta implementación proporciona un costo de tiempo log(n) garantizado para las operaciones containsKey, get, put y remove. Los algoritmos son adaptaciones de los que aparecen en Introduction to Algorithms de Cormen, Leiserson y Rivest.
Tenga en cuenta que el orden mantenido por un TreeMap, como cualquier mapa ordenado, y ya sea que se proporcione o no un comparador explícito, debe ser consistente con equals si este mapa ordenado va a implementar correctamente la interfaz Map. (Vea Comparable o Comparator para una definición precisa de consistente con equals). Esto es así porque la interfaz Map se define en términos de la operación equals, pero un mapa ordenado realiza todas las comparaciones de claves utilizando su método compareTo (o compare), por lo que dos claves que se consideran iguales por este método son, desde el punto de vista del mapa ordenado, iguales. El comportamiento de un mapa ordenado está bien definido incluso si su orden es inconsistente con equals; simplemente no obedece el contrato general de la interfaz Map.
7. Clase PriorityQueue
Queue procesa sus elementos en orden FIFO, pero a veces queremos que los elementos se procesen en función de su prioridad. Podemos usar PriorityQueue en este caso y necesitamos proporcionar una implementación de Comparator mientras creamos una instancia de PriorityQueue. PriorityQueue no permite valores nulos y no tiene límites. Para obtener más detalles sobre esto, dirígete a Java Priority Queue, donde puedes verificar su uso con un programa de muestra.
La clase Colecciones
La clase Colecciones de Java consta exclusivamente de métodos estáticos que operan sobre colecciones o las devuelven. Contiene algoritmos polimórficos que operan sobre colecciones, “envoltorios” que devuelven una nueva colección respaldada por una colección especificada y algunas otras cosas.
Esta clase contiene métodos para algoritmos de marco de recopilación, como búsqueda binaria , clasificación, mezcla, inversión, etc.
Envoltorios sincronizados
Los contenedores de sincronización agregan sincronización automática (seguridad de subprocesos) a una colección arbitraria. Cada una de las seis interfaces de colección principales (Collection, Set, List, Map, SortedSet y SortedMap) tiene un método de fábrica estático.
public static Collection synchronizedCollection(Collection c);public static Set synchronizedSet(Set s);public static List synchronizedList(List list);public static K,V MapK,V synchronizedMap(MapK,V m);public static SortedSet synchronizedSortedSet(SortedSet s);public static K,V SortedMapK,V synchronizedSortedMap(SortedMapK,V m);
Cada uno de estos métodos devuelve una colección sincronizada (segura para subprocesos) respaldada por la colección especificada.
Envoltorios no modificables
Los contenedores no modificables eliminan la posibilidad de modificar la colección interceptando todas las operaciones que modificarían la colección y lanzando un UnsupportedOperationException. Sus usos principales son:
- Hacer que una colección sea inmutable una vez que se ha creado. En este caso, es una buena práctica no mantener una referencia a la colección de respaldo. Esto garantiza absolutamente la inmutabilidad.
- Para permitir que ciertos clientes tengan acceso de solo lectura a sus estructuras de datos, mantenga una referencia a la colección de respaldo pero entregue una referencia al contenedor. De esta manera, los clientes pueden ver pero no modificar, mientras que usted mantiene acceso total.
public static Collection unmodifiableCollection(Collection? extends T c);public static Set unmodifiableSet(Set? extends T s);public static List unmodifiableList(List? extends T list);public static K,V MapK, V unmodifiableMap(Map? extends K, ? extends V m);public static SortedSet unmodifiableSortedSet(SortedSet? extends T s);public static K,V SortedMapK, V unmodifiableSortedMap(SortedMapK, ? extends V m);
Clases de colecciones seguras para subprocesos
El paquete Concurrent de Java 1.5 ( java.util.concurrent) contiene clases de colección seguras para subprocesos que permiten modificar las colecciones durante la iteración. Por diseño, un iterador es rápido y genera una excepción ConcurrentModificationException. Algunas de estas clases son CopyOnWriteArrayList, ConcurrentHashMap, CopyOnWriteArraySet.
- Evitar ConcurrentModificationException
- Ejemplo de CopyOnWriteArrayList
- Comparación entre HashMap y ConcurrentHashMap
Algoritmos de la API de colecciones
Java Collections Framework proporciona implementaciones de algoritmos que se utilizan comúnmente, como la ordenación y la búsqueda. La clase Collections contiene estas implementaciones de métodos. La mayoría de estos algoritmos funcionan en listas, pero algunos de ellos son aplicables a todo tipo de colecciones.
1. Ordenación
El algoritmo de ordenación reordena una lista de modo que sus elementos estén en orden ascendente según una relación de ordenación. Se proporcionan dos formas de la operación. La forma simple toma una lista y la ordena según el orden natural de sus elementos. La segunda forma de ordenación toma un comparador además de una lista y ordena los elementos con el comparador.
2. Barajar
El algoritmo Shuffle destruye cualquier rastro de orden que pueda haber estado presente en una lista. Es decir, este algoritmo reordena la lista basándose en la entrada de una fuente de aleatoriedad de modo que todas las permutaciones posibles ocurran con la misma probabilidad, suponiendo que la fuente de aleatoriedad sea justa. Este algoritmo es útil para implementar juegos de azar.
3. Búsqueda
El algoritmo binarySearch busca un elemento específico en una lista ordenada. Este algoritmo tiene dos formas. La primera toma una lista y un elemento a buscar (la “clave de búsqueda”).
Esta forma asume que la lista está ordenada en orden ascendente según el orden natural de sus elementos.
La segunda forma toma un Comparador además de la Lista y la clave de búsqueda y supone que la lista está ordenada en orden ascendente según el Comparador especificado.
El algoritmo de ordenación se puede utilizar para ordenar la lista antes de llamar a binarySearch.
4. Composición
Los algoritmos de frecuencia y disjuntos prueban algún aspecto de la composición de una o más colecciones.
- frecuencia : cuenta la cantidad de veces que aparece el elemento especificado en la colección especificada
- disjoint : determina si dos colecciones son disjuntas; es decir, si no contienen elementos en común
5. Valores mínimos y máximos
Los algoritmos min y max devuelven, respectivamente, el elemento mínimo y máximo contenido en una colección especificada. Ambas operaciones se presentan en dos formas. La forma simple toma solo una colección y devuelve el elemento mínimo (o máximo) de acuerdo con el orden natural de los elementos.
Funciones de la API de colecciones de Java 8
Los cambios más importantes de Java 8 están relacionados con las API de recopilación. Algunos de los cambios y mejoras más importantes son:
- Introducción de Stream API para procesamiento secuencial y paralelo; debe leer el Tutorial de Java Stream API para obtener más detalles.
- La interfaz iterable se ha ampliado con el método predeterminado forEach() para iterar sobre una colección.
- Las interfaces funcionales y de expresión Lambda son principalmente beneficiosas con las clases API de colección.
Cambios en la API de colecciones de Java 10
- Métodos List.copyOf, Set.copyOf y Map.copyOf para crear colecciones no modificables.
- La clase Collectors obtiene varios métodos para recopilar colecciones no modificables (Set, List, Map). Los nombres de estos métodos son toUnmodifiableList, toUnmodifiableSet y toUnmodifiableMap.
Veamos un ejemplo del uso de estos nuevos métodos de API de colecciones de Java 10 .
package com.journaldev.collections;import java.util.ArrayList;import java.util.List;import java.util.stream.Collectors;public class JDK10CollectionFunctions {public static void main(String[] args) {// 1. List, Set, Map copyOf(Collection) methodListString actors = new ArrayList();actors.add("Jack Nicholson");actors.add("Marlon Brando");System.out.println(actors);// prints [Jack Nicholson, Marlon Brando]// New API added - Creates an UnModifiable List from a List.ListString copyOfActors = List.copyOf(actors);System.out.println(copyOfActors);// prints [Jack Nicholson, Marlon Brando]// copyOfActors.add("Robert De Niro"); Will generate// UnsupportedOperationExceptionactors.add("Robert De Niro");System.out.println(actors);// prints [Jack Nicholson, Marlon Brando, Robert De Niro]System.out.println(copyOfActors);// prints [Jack Nicholson, Marlon Brando]// 2. Collectors class toUnmodifiableList, toUnmodifiableSet, and// toUnmodifiableMap methodsListString collect = actors.stream().collect(Collectors.toUnmodifiableList());System.out.println(collect);}}
Cambios en la API de colecciones de Java 11
Se agregó un nuevo método predeterminado toArray(IntFunctionT[] generator)a la interfaz Collection. Este método devuelve una matriz que contiene todos los elementos de esta colección y utiliza la función de generador proporcionada para asignar la matriz devuelta.
package com.journaldev.collections;import java.util.ArrayList;import java.util.Arrays;import java.util.List;public class JDK11CollectionFunctions {public static void main(String[] args) {/* * JDK 11 New Method in Collection interface * default T T[] toArray(IntFunctionT[] generator) { * return toArray(generator.apply(0)); } */ListString strList = new ArrayList();strList.add("Java");strList.add("Python");strList.add("Android");String[] strArray = strList.toArray(size - new String[size]);System.out.println(Arrays.toString(strArray));strArray = strList.toArray(size - new String[size + 5]);System.out.println(Arrays.toString(strArray));strArray = strList.toArray(size - new String[size * 3]);System.out.println(Arrays.toString(strArray));}}
Producción:
[Java, Python, Android][Java, Python, Android, null, null][Java, Python, Android]
Clases de colección en pocas palabras
La siguiente tabla proporciona detalles básicos de las clases de colección utilizadas comúnmente.
URL de descarga : Colección de clases de Java
| Recopilación | Realizar pedidos | Acceso aleatorio | Clave-valor | Elementos duplicados | Elemento nulo | Seguridad de los hilos |
|---|---|---|---|---|---|---|
| Lista de matrices | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ |
| Lista enlazada | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ |
| Conjunto de hash | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| Conjunto de árboles | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Mapa hash | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ |
| Mapa de árboles | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| Vector | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
| Tabla hash | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
| Propiedades | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
| Pila | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ |
| Copiar al escribir lista de matrices | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
| Mapa de hash concurrente | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
| Conjunto de matrices de copia al escribir | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
Espero que este tutorial haya explicado la mayoría de los temas del marco de colecciones de Java. Comparta su opinión en los comentarios.
Referencia: Documentación de Oracle