Si programas en Java, ya conoces java.util, aunque quizás no te hayas detenido a explorar todo lo que ofrece. Este paquete es uno de los más importantes de toda la plataforma Java: agrupa colecciones, fechas, generadores de números aleatorios, utilidades de formato, y mucho más. En este artículo recorremos sus usos más importantes con ejemplos prácticos para que puedas aprovecharlos al máximo.
¿Qué es java.util?
java.util es el paquete de utilidades de propósito general incluido en el JDK (Java Development Kit) desde sus primeras versiones. Contiene interfaces, clases abstractas y clases concretas que resuelven problemas comunes del día a día en el desarrollo de software: almacenar datos, iterar sobre ellos, ordenarlos, manejar fechas, generar aleatoriedad, observar cambios en objetos, y más.
No necesitas ninguna dependencia externa para usarlo, simplemente importas lo que necesitas:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Optional;
// etc.1. El Framework de Colecciones (Collections Framework)
Este es probablemente el uso más frecuente de java.util. El Collections Framework define un conjunto de interfaces y clases para manejar grupos de objetos de manera uniforme y eficiente.
1.1 List: listas ordenadas con duplicados
Una List es una secuencia ordenada de elementos. Permite duplicados y el acceso por índice.
ArrayList
Implementación basada en un array dinámico. Ideal cuando lees más de lo que insertas o eliminas.
import java.util.ArrayList;
import java.util.List;
List<String> frutas = new ArrayList<>();
frutas.add("Mango");
frutas.add("Piña");
frutas.add("Papaya");
frutas.add("Mango"); // permite duplicados
System.out.println(frutas.get(0)); // Mango
System.out.println(frutas.size()); // 4LinkedList
Implementación basada en nodos enlazados. Más eficiente para inserciones y eliminaciones frecuentes al inicio o al final de la lista.
import java.util.LinkedList;
LinkedList<Integer> numeros = new LinkedList<>();
numeros.addFirst(10);
numeros.addLast(20);
numeros.addFirst(5);
System.out.println(numeros); // [5, 10, 20]1.2 Set: conjuntos sin duplicados
Un Set no permite elementos repetidos. Útil cuando la unicidad importa más que el orden.
HashSet
Sin orden garantizado, muy rápido para búsquedas.
import java.util.HashSet;
import java.util.Set;
Set<String> paises = new HashSet<>();
paises.add("Panamá");
paises.add("Colombia");
paises.add("Panamá"); // duplicado ignorado
System.out.println(paises.size()); // 2LinkedHashSet
Mantiene el orden de inserción.
import java.util.LinkedHashSet;
Set<String> colores = new LinkedHashSet<>();
colores.add("Rojo");
colores.add("Verde");
colores.add("Azul");
System.out.println(colores); // [Rojo, Verde, Azul]TreeSet
Mantiene los elementos ordenados de forma natural (o mediante un Comparator).
import java.util.TreeSet;
Set<Integer> numeros = new TreeSet<>();
numeros.add(30);
numeros.add(10);
numeros.add(20);
System.out.println(numeros); // [10, 20, 30]1.3 Map: pares clave-valor
Un Map almacena pares de clave y valor. Cada clave es única, pero los valores pueden repetirse.
HashMap
El más usado. Sin orden garantizado, muy eficiente.
import java.util.HashMap;
import java.util.Map;
Map<String, Integer> edades = new HashMap<>();
edades.put("Ana", 28);
edades.put("Luis", 35);
edades.put("María", 22);
System.out.println(edades.get("Luis")); // 35
System.out.println(edades.containsKey("Ana")); // trueLinkedHashMap
Conserva el orden de inserción.
TreeMap
Mantiene las claves ordenadas.
import java.util.TreeMap;
Map<String, Integer> puntos = new TreeMap<>();
puntos.put("Carlos", 90);
puntos.put("Ana", 95);
puntos.put("Beatriz", 88);
System.out.println(puntos); // {Ana=95, Beatriz=88, Carlos=90}1.4 Queue y Deque: colas
Las colas siguen el principio FIFO (primero en entrar, primero en salir). Deque permite operaciones por ambos extremos.
import java.util.ArrayDeque;
import java.util.Queue;
Queue<String> cola = new ArrayDeque<>();
cola.offer("Tarea 1");
cola.offer("Tarea 2");
cola.offer("Tarea 3");
System.out.println(cola.poll()); // Tarea 1
System.out.println(cola.peek()); // Tarea 2 (sin remover)1.5 Stack: pilas LIFO
Aunque Stack existe en java.util, hoy se recomienda usar ArrayDeque como pila.
import java.util.ArrayDeque;
import java.util.Deque;
Deque<String> pila = new ArrayDeque<>();
pila.push("A");
pila.push("B");
pila.push("C");
System.out.println(pila.pop()); // C (último en entrar, primero en salir)1.6 PriorityQueue: cola con prioridad
Los elementos se extraen según su prioridad natural o un Comparator definido por el programador.
import java.util.PriorityQueue;
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.add(40);
pq.add(10);
pq.add(25);
System.out.println(pq.poll()); // 10 (el menor primero)2. La Clase Collections: algoritmos listos para usar
La clase java.util.Collections (con "s") es una clase utilitaria estática que provee algoritmos comunes sobre colecciones.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
List<Integer> lista = new ArrayList<>();
lista.add(5); lista.add(2); lista.add(8); lista.add(1);
Collections.sort(lista); // [1, 2, 5, 8]
Collections.reverse(lista); // [8, 5, 2, 1]
Collections.shuffle(lista); // orden aleatorio
System.out.println(Collections.max(lista)); // el mayor elemento
System.out.println(Collections.min(lista)); // el menor elemento
Collections.fill(lista, 0); // rellena todo con 0También puedes crear colecciones inmutables:
List<String> inmutable = Collections.unmodifiableList(lista);
// inmutable.add("algo"); // lanza UnsupportedOperationException3. La Clase Arrays: utilidades para arrays nativos
java.util.Arrays ofrece métodos estáticos para trabajar con arrays primitivos y de objetos.
import java.util.Arrays;
int[] numeros = {5, 3, 8, 1, 9, 2};
Arrays.sort(numeros);
System.out.println(Arrays.toString(numeros)); // [1, 2, 3, 5, 8, 9]
int indice = Arrays.binarySearch(numeros, 5); // búsqueda binaria
System.out.println(indice); // 3
int[] copia = Arrays.copyOf(numeros, 4); // copia los primeros 4
System.out.println(Arrays.toString(copia)); // [1, 2, 3, 5]
int[] relleno = new int[5];
Arrays.fill(relleno, 7);
System.out.println(Arrays.toString(relleno)); // [7, 7, 7, 7, 7]4. Optional: dile adiós al NullPointerException
Introducido en Java 8, java.util.Optional es un contenedor que puede o no tener un valor. Fuerza al programador a manejar explícitamente la posibilidad de ausencia de valor.
import java.util.Optional;
Optional<String> nombre = Optional.of("Claudia");
System.out.println(nombre.isPresent()); // true
System.out.println(nombre.get()); // Claudia
Optional<String> vacio = Optional.empty();
System.out.println(vacio.orElse("Sin nombre")); // Sin nombre
System.out.println(vacio.isPresent()); // false
// Con lógica encadenada
Optional<String> resultado = Optional.ofNullable(null);
resultado.ifPresentOrElse(
v -> System.out.println("Valor: " + v),
() -> System.out.println("No hay valor")
);5. Fechas y Tiempo: Date, Calendar y las modernas APIs
Históricamente java.util.Date y java.util.Calendar fueron las opciones para manejar fechas. Aunque siguen en uso (especialmente para compatibilidad), en Java 8+ se recomienda usar java.time. Sin embargo, es útil conocerlas.
Date
import java.util.Date;
Date ahora = new Date();
System.out.println(ahora); // fecha y hora actuales
System.out.println(ahora.getTime()); // milisegundos desde epochCalendar
import java.util.Calendar;
Calendar cal = Calendar.getInstance();
System.out.println(cal.get(Calendar.YEAR)); // año actual
System.out.println(cal.get(Calendar.MONTH) + 1); // mes (0-indexado)
System.out.println(cal.get(Calendar.DAY_OF_MONTH));
cal.add(Calendar.DAY_OF_MONTH, 10); // sumar 10 días6. Random: generación de números aleatorios
java.util.Random permite generar números pseudoaleatorios de distintos tipos.
import java.util.Random;
Random rand = new Random();
System.out.println(rand.nextInt(100)); // entero entre 0 y 99
System.out.println(rand.nextDouble()); // double entre 0.0 y 1.0
System.out.println(rand.nextBoolean()); // true o false
System.out.println(rand.nextLong()); // long aleatorio
// Con semilla (reproducible)
Random semillado = new Random(42);
System.out.println(semillado.nextInt(50)); // siempre el mismo resultadoDesde Java 17 también puedes usar RandomGenerator para acceso a algoritmos modernos como Xoshiro o LXM.
7. Scanner: lectura de entradas
java.util.Scanner simplifica la lectura de texto desde distintas fuentes: la consola, archivos, cadenas de texto, streams, etc.
import java.util.Scanner;
Scanner sc = new Scanner(System.in);
System.out.print("Escribe tu nombre: ");
String nombre = sc.nextLine();
System.out.println("Hola, " + nombre + "!");
// Leer números
System.out.print("Escribe un número: ");
int numero = sc.nextInt();
System.out.println("El doble es: " + (numero * 2));
sc.close();También puede leer desde una cadena:
Scanner scTexto = new Scanner("10 20 30");
while (scTexto.hasNextInt()) {
System.out.println(scTexto.nextInt());
}8. StringTokenizer: dividir cadenas de texto
Aunque hoy se prefiere String.split() o la API de Stream, java.util.StringTokenizer sigue siendo eficiente para dividir cadenas por delimitadores.
import java.util.StringTokenizer;
String frase = "Java es un lenguaje poderoso";
StringTokenizer st = new StringTokenizer(frase);
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
// Java
// es
// un
// lenguaje
// poderoso9. Properties: configuración clave-valor
java.util.Properties extiende Hashtable y es ideal para manejar archivos de configuración .properties.
import java.util.Properties;
Properties config = new Properties();
config.setProperty("app.nombre", "MiApp");
config.setProperty("app.version", "1.0");
config.setProperty("app.autor", "Claudia");
System.out.println(config.getProperty("app.nombre")); // MiApp
System.out.println(config.getProperty("app.version")); // 1.0
System.out.println(config.getProperty("no.existe", "valor por defecto")); // valor por defecto10. Timer y TimerTask: tareas programadas
Permiten ejecutar código después de un retardo o de forma periódica. Para proyectos grandes se recomienda ScheduledExecutorService, pero Timer sigue siendo útil para casos simples.
import java.util.Timer;
import java.util.TimerTask;
Timer timer = new Timer();
TimerTask tarea = new TimerTask() {
@Override
public void run() {
System.out.println("Tarea ejecutada: " + new java.util.Date());
}
};
// Ejecutar después de 2 segundos, cada 3 segundos
timer.scheduleAtFixedRate(tarea, 2000, 3000);
// Para detenerlo:
// timer.cancel();11. Observer / Observable: patrón de observador
Aunque depreciadas desde Java 9, las clases Observable y la interfaz Observer de java.util implementan el patrón de diseño Observer: un objeto notifica a sus suscriptores cuando cambia su estado. Hoy es mejor usar PropertyChangeListener o frameworks reactivos, pero vale conocerlas.
// Observable (modelo)
import java.util.Observable;
public class Temperatura extends Observable {
private double valor;
public void setValor(double valor) {
this.valor = valor;
setChanged();
notifyObservers(valor);
}
}
// Observer (vista)
import java.util.Observer;
public class Termometro implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("Temperatura actualizada: " + arg + "°C");
}
}12. Formatter: formateo avanzado de texto
java.util.Formatter proporciona formato de cadenas de texto similar a printf de C. En la práctica se usa a través de String.format() o System.out.printf().
import java.util.Formatter;
Formatter fmt = new Formatter();
fmt.format("Nombre: %-10s | Edad: %3d | Sueldo: $%,.2f%n", "Ana", 28, 2500.5);
System.out.print(fmt); // Nombre: Ana | Edad: 28 | Sueldo: $2,500.50
// Forma más común:
String resultado = String.format("Pi es aproximadamente %.4f", Math.PI);
System.out.println(resultado); // Pi es aproximadamente 3.141613. Locale: internacionalización
java.util.Locale representa un entorno cultural y lingüístico. Fundamental para aplicaciones multilenguaje.
import java.util.Locale;
import java.text.NumberFormat;
Locale espana = new Locale("es", "ES");
Locale panama = new Locale("es", "PA");
NumberFormat nf = NumberFormat.getCurrencyInstance(espana);
System.out.println(nf.format(1234567.89)); // 1.234.567,89 €
NumberFormat nfUs = NumberFormat.getCurrencyInstance(Locale.US);
System.out.println(nfUs.format(1234567.89)); // $1,234,567.8914. ResourceBundle: mensajes localizados
Junto con Locale, java.util.ResourceBundle permite cargar mensajes desde archivos .properties según el idioma del usuario.
// Archivo: messages_es.properties
// saludo=Hola, mundo
// Archivo: messages_en.properties
// saludo=Hello, world
import java.util.Locale;
import java.util.ResourceBundle;
ResourceBundle bundle = ResourceBundle.getBundle("messages", new Locale("es"));
System.out.println(bundle.getString("saludo")); // Hola, mundo
ResourceBundle bundleEn = ResourceBundle.getBundle("messages", Locale.ENGLISH);
System.out.println(bundleEn.getString("saludo")); // Hello, world15. Objects: utilidades para manejo de objetos
La clase java.util.Objects (desde Java 7) ofrece métodos estáticos para comparar objetos, verificar nulos, calcular hash codes, y más de forma segura.
import java.util.Objects;
String a = "Java";
String b = null;
System.out.println(Objects.isNull(b)); // true
System.out.println(Objects.nonNull(a)); // true
System.out.println(Objects.toString(b, "N/A")); // N/A
// Comparación segura sin NullPointerException
System.out.println(Objects.equals(a, b)); // false
// requireNonNull para validaciones
String nombre = Objects.requireNonNull(a, "El nombre no puede ser nulo");
System.out.println(nombre); // Java16. BitSet: conjuntos de bits
java.util.BitSet permite manejar secuencias de bits de forma eficiente. Útil en algoritmos de búsqueda, criptografía básica, o representación compacta de conjuntos booleanos.
import java.util.BitSet;
BitSet bits = new BitSet(16);
bits.set(1);
bits.set(3);
bits.set(5);
bits.set(7);
System.out.println(bits); // {1, 3, 5, 7}
System.out.println(bits.get(3)); // true
System.out.println(bits.get(4)); // false
bits.clear(3);
System.out.println(bits); // {1, 5, 7}Tabla resumen de las clases más usadas
| Clase / Interfaz | Categoría | Uso principal |
|---|---|---|
ArrayList | Colección | Lista dinámica de acceso rápido por índice |
LinkedList | Colección | Lista con inserciones/eliminaciones eficientes |
HashSet | Colección | Conjunto sin duplicados, sin orden |
TreeSet | Colección | Conjunto ordenado de forma natural |
HashMap | Mapa | Pares clave-valor, sin orden |
TreeMap | Mapa | Pares clave-valor ordenados por clave |
PriorityQueue | Cola | Cola con extracción según prioridad |
Optional | Utilidad | Evitar NullPointerException |
Collections | Algoritmos | Ordenar, invertir, buscar en colecciones |
Arrays | Algoritmos | Ordenar, copiar, buscar en arrays nativos |
Scanner | E/S | Leer datos desde consola, archivo o cadena |
Random | Utilidad | Generar números pseudoaleatorios |
Date / Calendar | Fechas | Manejo de fechas (legacy) |
Locale | i18n | Internacionalización y localización |
Properties | Configuración | Archivos de configuración clave-valor |
Timer / TimerTask | Concurrencia | Tareas programadas o periódicas |
Objects | Utilidad | Comparaciones y verificaciones de nulos seguras |
BitSet | Datos | Representación compacta de bits |
Conclusión
java.util es mucho más que un simple paquete de colecciones. Es un ecosistema completo de herramientas que resuelve docenas de problemas cotidianos del programador Java sin necesidad de librerías externas. Conocerlo a fondo te hace un desarrollador más eficiente, capaz de elegir la herramienta correcta para cada situación.
Si estás aprendiendo Java, te recomiendo empezar por ArrayList, HashMap y Optional, y luego ir explorando el resto a medida que los necesites. Y si ya eres un desarrollador experimentado, quizás este artículo te recordó alguna clase que no usabas hace tiempo.
¿Tienes alguna clase favorita de java.util? ¡Déjamelo saber en los comentarios!
No hay comentarios todavía. Sé el primero en compartir tu opinión.