Java.util: La Navaja Suiza de Java — Todo lo que Necesitas Saber

D
DanisCh
(Actualizado: ) 11 min de lectura
Java.util: La Navaja Suiza de Java — Todo lo que Necesitas Saber

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()); // 4

LinkedList

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()); // 2

LinkedHashSet

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")); // true

LinkedHashMap

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 0

También puedes crear colecciones inmutables:

List<String> inmutable = Collections.unmodifiableList(lista);
// inmutable.add("algo"); // lanza UnsupportedOperationException

3. 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 epoch

Calendar

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ías

6. 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 resultado

Desde 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
// poderoso

9. 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 defecto

10. 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.1416

13. 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.89

14. 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, world

15. 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); // Java

16. 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 / InterfazCategoríaUso principal
ArrayListColecciónLista dinámica de acceso rápido por índice
LinkedListColecciónLista con inserciones/eliminaciones eficientes
HashSetColecciónConjunto sin duplicados, sin orden
TreeSetColecciónConjunto ordenado de forma natural
HashMapMapaPares clave-valor, sin orden
TreeMapMapaPares clave-valor ordenados por clave
PriorityQueueColaCola con extracción según prioridad
OptionalUtilidadEvitar NullPointerException
CollectionsAlgoritmosOrdenar, invertir, buscar en colecciones
ArraysAlgoritmosOrdenar, copiar, buscar en arrays nativos
ScannerE/SLeer datos desde consola, archivo o cadena
RandomUtilidadGenerar números pseudoaleatorios
Date / CalendarFechasManejo de fechas (legacy)
Localei18nInternacionalización y localización
PropertiesConfiguraciónArchivos de configuración clave-valor
Timer / TimerTaskConcurrenciaTareas programadas o periódicas
ObjectsUtilidadComparaciones y verificaciones de nulos seguras
BitSetDatosRepresentació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!

¿Te ha gustado esta entrada?

Compártela con tus compañeros para que también sigan aprendiendo.

Comunidad y Comentarios

0 COMENTARIOS

No hay comentarios todavía. Sé el primero en compartir tu opinión.

Escribe tu opinión
Respondiendo a