Explorando la Interfaz Set y sus Implementaciones en Java

La interfaz Set, al igual que List, hereda de Collection y comparte gran parte de su funcionalidad. Sin embargo, la diferencia fundamental radica en que Set no permite elementos duplicados y, por naturaleza, no garantiza un orden específico para sus elementos.

Implementaciones Clave de Set

HashSet

HashSet se basa en la implementación de HashMap para su funcionamiento. Utiliza el valor hash de los objetos para determinar su ubicación de almacenamiento, lo que resulta en un rendimiento eficiente para las operaciones de adición, recuperación y búsqueda.

Ejemplo de Código con HashSet


HashSet<string> nombresUnicos = new HashSet<>();
nombresUnicos.add("Ana");
nombresUnicos.add("Luis");
nombresUnicos.add("Carlos");
nombresUnicos.add("Carlos"); // Este elemento duplicado será ignorado

// Iterando sobre los elementos para mostrarlos
Iterator<string> iterador = nombresUnicos.iterator();
while (iterador.hasNext()) {
   String nombre = iterador.next();
   System.out.println(nombre);
}
   </string></string>

Resultado de la ejecución:


Carlos
Ana
Luis
   

Como se observa en la salida, el orden de los elementos recuperados no coincide con el orden en que fueron añadidos, lo que demuestra la naturaleza desordenada de Set. Además, la cadena duplicada "Carlos" solo se almacena una vez, validando la propiedad de no permitir duplicados.

El mecanismo de HashSet para evitar duplicados implica los siguientes pasos al añadir un elemento:

  1. Se invoca el método hashCode() del objeto a añadir para obtener su valor hash. Este valor se utiliza para calcular una posición de almacenamiento tentativa.
  2. Si la posición calculada está vacía, el elemento se almacena directamente.
  3. Si la posición ya contiene elementos, se invoca el método equals() para comparar el nuevo elemento con los elementos existentes en esa posición. Si equals() devuelve false para todos, el nuevo elemento se añade. Si devuelve true para alguno, el elemento se considera duplicado y no se añade.

Consideraciones para Almacenar Objetos Personalizados en HashSet

Para que HashSet maneje correctamente la unicidad de objetos personalizados, es crucial sobrescribir tanto el método hashCode() como el método equals() en la clase del objeto.

Definición de una clase Estudiante:


public class Estudiante {
   private String idEstudiante;
   private String nombreCompleto;

   public Estudiante() {}

   public Estudiante(String idEstudiante, String nombreCompleto) {
       this.idEstudiante = idEstudiante;
       this.nombreCompleto = nombreCompleto;
   }

   @Override
   public String toString() {
       return idEstudiante + ": " + nombreCompleto;
   }

   @Override
   public int hashCode() {
       // Usamos el idEstudiante para el hash
       return idEstudiante.hashCode();
   }

   @Override
   public boolean equals(Object obj) {
       if (this == obj) return true; // Misma instancia
       if (obj == null || getClass() != obj.getClass()) return false; // Null o tipo incorrecto
       Estudiante otroEstudiante = (Estudiante) obj;
       // Comparación basada en idEstudiante
       return idEstudiante.equals(otroEstudiante.idEstudiante);
   }
}
   

Clase de prueba:


import java.util.HashSet;

public class PruebaHashSet {
   public static void main(String[] args) {
       HashSet<estudiante> estudiantes = new HashSet<>();
       Estudiante est1 = new Estudiante("001", "Juan Pérez");
       Estudiante est2 = new Estudiante("001", "Juan Pérez"); // Mismo ID
       Estudiante est3 = new Estudiante("002", "Ana Gómez");

       estudiantes.add(est1);
       estudiantes.add(est2); // Debería ser ignorado si hashCode y equals están bien implementados
       estudiantes.add(est3);

       System.out.println(estudiantes);
   }
}
   </estudiante>

Si hashCode() y equals() se sobrescriben correctamente basándose en un identificador único (como idEstudiante), la salida esperada sería:


[001: Juan Pérez, 002: Ana Gómez] 
   

Si no se sobrescriben estos métodos, HashSet trataría los objetos como distintos, incluso si sus atributos son idénticos, y los añadiría ambos.

TreeSet

TreeSet es una implementación de Set que mantiene sus elementos ordenados. Internamente, se basa en TreeMap. Garantiza que los elementos estén libres de duplicados y que se almacenen en un orden natural o según un comparador proporcionado.

Ejemplo de Código con TreeSet


TreeSet<string> palabrasOrdenadas = new TreeSet<>();
palabrasOrdenadas.add("Beta");
palabrasOrdenadas.add("Delta");
palabrasOrdenadas.add("Alfa");
palabrasOrdenadas.add("Gamma");

System.out.println(palabrasOrdenadas);
   </string>

Resultado de la ejecución:


[Alfa, Beta, Delta, Gamma]
   

La salida muestra que los elementos se almacenan en orden alfabético, no en el orden en que se añadieron.

Almacenar Objetos Personalizados en TreeSet

Para que TreeSet pueda ordenar objetos personalizados, la clase de estos objetos debe implementar la interfaz Comparable, proporcionando una forma de comparar sus instancias. Aletrnativamente, se puede pasar un Comparator al constructor de TreeSet.

Deifnición de una clase Producto que implementa Comparable:


public class Producto implements Comparable<producto> {
   private int codigo;
   private String nombre;

   public Producto() {}

   public Producto(int codigo, String nombre) {
       this.codigo = codigo;
       this.nombre = nombre;
   }

   @Override
   public String toString() {
       return codigo + ": " + nombre;
   }

   // Método de comparación para ordenar por código
   @Override
   public int compareTo(Producto otro) {
       return Integer.compare(this.codigo, otro.codigo);
   }

   // Nota: Al implementar Comparable, TreeSet usa compareTo.
   // Si se necesita igualdad para evitar duplicados, equals también debería
   // ser coherente con compareTo, aunque para TreeSet la comparación define la unicidad.
}
   </producto>

Programa principal:


import java.util.TreeSet;

public class PruebaTreeSet {
   public static void main(String[] args) {
       TreeSet<producto> inventario = new TreeSet<>();
       Producto prod1 = new Producto(101, "Laptop");
       Producto prod2 = new Producto(103, "Teclado");
       Producto prod3 = new Producto(102, "Mouse");

       inventario.add(prod1);
       inventario.add(prod2);
       inventario.add(prod3);

       System.out.println(inventario);
   }
}
   </producto>

Resultado esperado (ordenado por código):


[101: Laptop, 102: Mouse, 103: Teclado]
   

Si la clase Producto no implementara Comparable, intentar añadir objetos a un TreeSet rseultaría en una ClassCastException, ya que la JVM no sabría cómo ordenar los objetos.

Etiquetas: java set HashSet TreeSet comparable

Publicado el 7-26 06:39