29/06/2015
En el vasto universo del Java Collections Framework, existen diversas herramientas diseñadas para almacenar y manipular grupos de objetos. Entre ellas, el TreeSet destaca como una implementación especializada de la interfaz Set que ofrece una característica fundamental: mantiene sus elementos ordenados. Si alguna vez has necesitado una colección que no solo garantice la unicidad de sus elementos, sino que también los mantenga en un orden predecible, entonces TreeSet es la estructura de datos que estabas buscando.

A diferencia de otras implementaciones como HashSet, que no ofrece ninguna garantía sobre el orden de sus elementos, un TreeSet organiza automáticamente su contenido. Esta capacidad no es magia, sino el resultado de su ingeniosa implementación interna, que lo convierte en una opción poderosa para escenarios específicos donde el orden es tan importante como la unicidad.
¿Qué es Exactamente un TreeSet en Java?
Un TreeSet es una clase que forma parte del paquete java.util e implementa la interfaz NavigableSet, que a su vez extiende de SortedSet. En términos sencillos, es una colección que almacena elementos únicos de forma ordenada. Para lograr esto, internamente utiliza una estructura de datos de árbol autobalanceado, específicamente un Árbol Rojo-Negro (Red-Black Tree), a través de una instancia de TreeMap.
Estas son sus características más importantes:
- Almacena elementos únicos: Al igual que cualquier
Set, no permite elementos duplicados. Si intentas agregar un elemento que ya existe, la operación simplemente será ignorada. - Ordena los elementos: Los elementos se ordenan de forma ascendente según su orden natural (si la clase implementa la interfaz
Comparable) o mediante unComparatorproporcionado en el momento de la creación delTreeSet. - No es seguro para hilos (not thread-safe): Si múltiples hilos acceden y modifican un
TreeSetconcurrentemente, su integridad puede verse comprometida. Para entornos multifilamento, debe ser sincronizado externamente. - Rendimiento logarítmico: Las operaciones básicas como agregar (
add), eliminar (remove) y buscar (contains) tienen un costo de tiempo garantizado de O(log n), donde 'n' es el número de elementos en el conjunto. - No permite elementos nulos: A partir de Java 7, intentar agregar un elemento
nulla unTreeSetlanzará una excepciónNullPointerException, ya quenullno puede ser comparado con otros elementos para determinar su posición en el árbol.
Creando y Usando un TreeSet
Para empezar a usar un TreeSet, primero debes importarlo desde el paquete java.util. La creación es sencilla y directa.
Creación Básica
Puedes crear un TreeSet que ordenará sus elementos según el orden natural de los mismos (por ejemplo, alfabético para Strings, numérico para Integers).

import java.util.TreeSet; // Crear un TreeSet para almacenar Strings TreeSet<String> nombres = new TreeSet<>(); // Crear un TreeSet para almacenar números enteros TreeSet<Integer> numeros = new TreeSet<>();Creación con un Comparator
Si necesitas un criterio de ordenación personalizado, puedes pasar una instancia de Comparator al constructor. Por ejemplo, para ordenar cadenas de texto por su longitud en lugar de alfabéticamente:
import java.util.TreeSet; import java.util.Comparator; // Crear un TreeSet que ordena las cadenas por su longitud TreeSet<String> palabras = new TreeSet<>(Comparator.comparing(String::length)); palabras.add("Java"); palabras.add("Colecciones"); palabras.add("Set"); // La salida será: [Set, Java, Colecciones] System.out.println(palabras);Operaciones Fundamentales con TreeSet
Una vez creado tu TreeSet, puedes realizar una serie de operaciones comunes para gestionarlo.
1. Agregar Elementos con add()
El método add() inserta un elemento en el conjunto. Si el elemento ya existe, no se realiza ningún cambio y el método devuelve false. Si se añade con éxito, devuelve true.
TreeSet<String> coches = new TreeSet<>(); coches.add("Volvo"); coches.add("BMW"); coches.add("Ford"); coches.add("BMW"); // Este duplicado será ignorado coches.add("Mazda"); // Los elementos se imprimen en orden alfabético // Salida: [BMW, Ford, Mazda, Volvo] System.out.println(coches);2. Comprobar la Existencia de un Elemento con contains()
Para verificar si un elemento está presente en el conjunto, se utiliza el método contains(), que devuelve true o false.
boolean tieneBmw = coches.contains("BMW"); // Devuelve true boolean tieneAudi = coches.contains("Audi"); // Devuelve false3. Eliminar Elementos con remove()
Puedes eliminar un elemento específico usando el método remove().
coches.remove("Ford"); // Salida: [BMW, Mazda, Volvo] System.out.println(coches);4. Obtener el Tamaño con size()
El método size() devuelve el número de elementos únicos en el TreeSet.

int cantidad = coches.size(); // Devuelve 35. Limpiar el Conjunto con clear()
Para eliminar todos los elementos de una sola vez, utiliza el método clear().
coches.clear(); System.out.println(coches.isEmpty()); // Devuelve trueUna de las grandes ventajas de TreeSet es su capacidad para ser recorrido de forma ordenada y para navegar por sus elementos.
Recorrer el TreeSet
La forma más común de iterar es con un bucle for-each, que recorrerá los elementos en orden ascendente.
TreeSet<Integer> numeros = new TreeSet<>(); numeros.add(40); numeros.add(10); numeros.add(30); numeros.add(20); // Se imprimirán los números en orden: 10, 20, 30, 40 for (int n: numeros) { System.out.println(n); }También puedes usar un iterator() para recorrer en orden ascendente o un descendingIterator() para hacerlo en orden descendente.
Al implementar NavigableSet, TreeSet ofrece métodos potentes para acceder a elementos y subconjuntos:
first(): Devuelve el primer elemento (el más bajo) del conjunto.last(): Devuelve el último elemento (el más alto) del conjunto.headSet(toElement): Devuelve una vista del conjunto con todos los elementos que son estrictamente menores quetoElement.tailSet(fromElement): Devuelve una vista del conjunto con todos los elementos que son mayores o iguales afromElement.subSet(fromElement, toElement): Devuelve una vista del conjunto con los elementos en el rango [fromElement,toElement).fromElementes inclusivo ytoElementes exclusivo.
Estos métodos son extremadamente útiles para obtener porciones específicas de datos ordenados sin tener que iterar por toda la colección.

Comparativa: TreeSet vs. HashSet
La elección entre TreeSet y HashSet es una de las decisiones más comunes al trabajar con conjuntos. La siguiente tabla resume sus diferencias clave:
| Característica | HashSet | TreeSet |
|---|---|---|
| Orden | Sin orden garantizado. | Elementos ordenados (naturalmente o por Comparator). |
| Implementación Interna | Tabla hash (HashMap). | Árbol Rojo-Negro (TreeMap). |
| Rendimiento | Más rápido. Operaciones en tiempo constante O(1) en promedio. | Más lento. Operaciones en tiempo logarítmico O(log n). |
| Uso de Memoria | Generalmente menor. | Mayor, debido a la sobrecarga de la estructura de árbol. |
| Elementos Nulos | Permite un elemento nulo. | No permite elementos nulos. |
En resumen: elige HashSet cuando la velocidad sea tu máxima prioridad y no te importe el orden. Elige TreeSet cuando necesites que tus elementos únicos estén siempre ordenados.
Preguntas Frecuentes (FAQ)
¿Por qué TreeSet no permite elementos nulos?
TreeSet necesita comparar los elementos entre sí para mantenerlos ordenados. El valor null no puede ser comparado con ningún otro objeto (intentarlo resultaría en una NullPointerException), por lo que la estructura no puede determinar dónde ubicarlo en el árbol. Por esta razón, su inserción está prohibida.
¿Es TreeSet seguro para hilos (thread-safe)?
No, TreeSet no está sincronizado. Si necesitas usarlo en un entorno concurrente, debes gestionarlo manualmente o, preferiblemente, envolverlo utilizando el método Collections.synchronizedSortedSet(). SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));
¿Qué significa que el orden debe ser "consistente con equals"?
La interfaz Set se define en términos del método equals() para determinar la unicidad. Sin embargo, TreeSet usa su método de comparación (compareTo o compare) para todas sus operaciones, incluida la comprobación de igualdad. Si dos objetos son considerados iguales por el comparador (compare(a, b) == 0) pero no por equals() (a.equals(b) es false), el TreeSet los tratará como un solo elemento, lo cual viola el contrato general de la interfaz Set. Por ello, se recomienda que el criterio de ordenación sea consistente con la lógica de igualdad.
Conclusión
El TreeSet es una herramienta invaluable en el arsenal de cualquier desarrollador de Java. Ofrece una solución elegante y eficiente para el manejo de colecciones de elementos únicos y ordenados. Aunque su rendimiento es ligeramente inferior al de HashSet debido a la sobrecarga de mantener el orden, sus capacidades de navegación y su ordenación automática lo convierten en la opción perfecta para escenarios donde estas características son cruciales. Comprender cuándo y cómo usarlo te permitirá escribir un código más limpio, predecible y potente.
Si quieres conocer otros artículos parecidos a TreeSet en Java: Colecciones Ordenadas y Únicas puedes visitar la categoría Juegos.
