Colecciones de Java: la interfaz Set

Java Collections Framework es un marco fundamental y esencial que cualquier desarrollador de Java fuerte debe conocer como la palma de su mano. Una colección que...

Introducción

El Marco de colecciones de Java es un marco fundamental y esencial que cualquier desarrollador fuerte de Java debe conocer como el dorso de su mano.

Una Colección en Java se define como un grupo o colección de objetos individuales que actúan como un solo objeto.

Hay muchas clases de colección en Java y todas ellas amplían las interfaces java.util.Collection y java.util.Map. Estas clases en su mayoría ofrecen diferentes formas de formular una colección de objetos dentro de un solo objeto.

Java Collections es un marco que proporciona numerosas operaciones sobre una colección: búsqueda, clasificación, inserción, manipulación, eliminación, etc.

Esta es la primera parte de una serie de artículos de colecciones de Java:

Conjuntos

La siguiente interfaz común del marco es java.util.Set.

Los conjuntos no ofrecen métodos adicionales, aparte de los métodos heredados de la interfaz Colección.

Un Conjunto modela la abstracción del conjunto matemático y no puede contener elementos duplicados. Dicho esto, también vale la pena señalar que estos elementos no tienen un orden específico dentro del conjunto:

1
2
3
4
5
List<String> names = Arrays.asList("David", "Scott", "Adam", "Jane", "Scott", "David", "Usman");
System.out.println(names);

Set<String> uniqueNames = new HashSet<>(names);
System.out.println(uniqueNames);

Ejecutar este fragmento de código produciría:

1
2
[David, Scott, Adam, Jane, Scott, David, Usman]
[Adam, David, Jane, Scott, Usman]

Como puede notar, la lista names contiene entradas duplicadas, y el conjunto uniqueNames elimina las duplicadas y las imprime sin un orden específico.

Adición de un elemento

Usando el método add(), similar a las Listas, podemos agregar objetos a Set:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Set<String> uniqueNames = new HashSet<>();
uniqueNames.add("David");
uniqueNames.add("Scott");
uniqueNames.add("Adam");
uniqueNames.add("Jane");
uniqueNames.add("Scott");
uniqueNames.add("David");
uniqueNames.add("Usman");

System.out.println(uniqueNames);

Ejecutar este fragmento de código producirá:

1
[Adam, David, Jane, Scott, Usman]

Eliminación de elementos

Usando el método booleano remove(), podemos eliminar el elemento especificado de este conjunto si está presente:

1
2
3
System.out.println(uniqueNumbers.remove(2));

System.out.println(uniqueNumbers);

Producción:

1
2
true
[1, 3]

Otra opción es utilizar el método clear() para eliminar todos los elementos del Conjunto:

1
2
3
4
5
List<String> names = Arrays.asList("David", "Scott", "Adam", "Jane", "Scott", "David", "Usman");
Set<String> uniqueNames = new HashSet<>(names);

uniqueNames.clear();
System.out.println(uniqueNames);

Ejecutar este fragmento de código produciría:

1
[]

Alternativamente, podríamos confiar en el método removeAll():

1
2
3
4
5
6
List<String> names = Arrays.asList("David", "Scott", "Adam", "Jane", "Scott", "David", "Usman");
List<String> newNames = Arrays.asList("David", "Adam");
Set<String> uniqueNames = new HashSet<>(names);

uniqueNames.removeAll(newNames);
System.out.println(uniqueNames);

Ejecutar este fragmento de código produciría:

1
[Jane, Scott, Usman]

Es importante notar que el método removeAll() acepta una Colección como argumento. Esto se puede usar para eliminar todos los elementos comunes de dos colecciones diferentes, en este caso una ‘Lista’ y un ‘Conjunto’.

También tenga en cuenta que puede usar este método para eliminar todos los elementos de la misma Colección:

1
uniqueName.removeAll(uniqueNames);

Esto, por supuesto, terminará con un conjunto vacío. Sin embargo, este enfoque no se recomienda ya que llamar al método removeAll() cuesta mucho más que el método clear().

Esto se debe al método removeAll() que compara cada elemento de la colección de argumentos con la colección que llama al método, mientras que clear() simplemente los apunta a todos a null y establece el tamaño en 0.

Contiene el elemento

Usando el método booleano contains() con el objeto dado, podemos verificar si este Conjunto contiene un elemento específico:

1
2
3
4
5
6
7
List<String> names = Arrays.asList("David", "Scott", "Adam", "Jane", "Scott", "David", "Usman");
Set<String> uniqueNames = new HashSet<>(names);

System.out.println(uniqueNames.contains("David"));
System.out.println(uniqueNames.contains("Scott"));
System.out.println(uniqueNames.contains("Adam"));
System.out.println(uniqueNames.contains("Andrew"));

Ejecutar este código produciría:

1
2
3
4
true
true
true
false

Elementos de iteración {#elementos de iteración}

Lo mismo que con las listas, aunque es posible iterar con bucles for y enhanced-for, es mejor usar el Iterator de las colecciones de Java para esta tarea:

1
2
3
4
5
6
7
Set<E> set = new TreeSet<E>();
...
for(Iterator<E> iterator = set.iterator(); iterator.hasNext()) {
    E element = iterator.next();
    element.someMethod();
    iterator.remove(element);
}

Además, Java 8 nos presenta una manera realmente simple de imprimir los elementos usando referencias de métodos:

1
set.forEach(System.out::println);

Tamaño de recuperación {#tamaño de recuperación}

Si desea recuperar el tamaño de un conjunto:

1
2
3
4
List<String> names = Arrays.asList("David", "Scott", "Adam", "Jane", "Scott", "David", "Usman");
Set<String> uniqueNames = new HashSet<>(names);

System.out.println(uniqueNames.size());

Ejecutar este fragmento de código produciría:

1
5

Comprobando si está vacío

Si desea ejecutar una verificación para ver si un conjunto está vacío o no antes de realizar cualquier operación en él:

1
2
3
4
List<String> names = Arrays.asList("David", "Scott", "Adam", "Jane", "Scott", "David", "Usman");
Set<String> uniqueNames = new HashSet<>(names);

System.out.println(uniqueNames.isEmpty());

Ejecutar este fragmento de código produciría:

1
false

Implementaciones y diferencias {#implementaciones y diferencias}

Conjunto hash:

  • Basado en HashMap (llama a hash code() en el elemento y busca la ubicación)
  • Buena implementación de propósito general (Cambia el tamaño cuando se queda sin espacio)

Conjunto de árboles:

  • Basado en TreeMap (Usa un árbol binario con un orden de clasificación requerido)
  • Mantiene los elementos en el orden dado

Conjuntos de enumeración:

  • Implementación especializada para enumeraciones (utiliza un conjunto de bits basado en el ordinal de la enumeración)
  • Usar al almacenar conjuntos de enumeraciones

Comparación algorítmica

comparación_rendimiento_hashset_treeset_enumset

Conclusión

El marco Java Collections es un marco fundamental que todo desarrollador de Java debería saber cómo usar.

En el artículo, hemos hablado de Set Interface y sus implementaciones, sus ventajas y desventajas, así como las operaciones que seguramente utilizará en un momento u otro.

Si está interesado en leer más sobre las interfaces de colección, continúe leyendo - Colecciones de Java: Colas, Deques y Pilas (próximamente).

Licensed under CC BY-NC-SA 4.0