¿Cómo iterar sobre un Set / HashSet sin un iterador?

273

¿Cómo puedo iterar sobre a Set/ HashSetsin lo siguiente?

Iterator iter = set.iterator();
while (iter.hasNext()) {
    System.out.println(iter.next());
}
usuario1621988
fuente
31
¿Realmente estás tratando de evitar el uso de un iterador, o simplemente no quieres verlo en tu código fuente?
Jon Skeet
2
Puede hacer que el código sea más corto y limpio, pero debe usar un iterador. ¿Hay alguna razón por la que quieras evitarlo?
Peter Lawrey
55
Solo para el registro y como explicación de por qué evitar el iterador: actualmente estoy enfrentando ese problema en un juego de Android escrito en Java. En este momento estoy usando un HashSet para almacenar oyentes que necesitan ser notificados regularmente sobre eventos. Sin embargo, la repetición repetida de las colecciones provoca una gran actividad de recolección de basura que puede cargar el ciclo del juego. Y eso es un no-go en un juego de Java. Voy a reescribir estas partes.
tiguchi
12
@thecoshman Estoy hablando solo desde una perspectiva de desarrollo de juegos Java donde quieres evitar GC durante las actualizaciones regulares del estado del juego a toda costa. Los iteradores son solo objetos útiles temporalmente, ya que no puede restablecerlos al inicio, por lo tanto, se recrean en cada llamada al método de iteración (consulte la fuente ArrayList.java, por ejemplo). Si se usa en un bucle de juego para iterar objetos de escena y considerando al menos 30 actualizaciones por segundo, terminará con al menos 60 objetos iteradores (la escena se repite dos veces por ciclo) por segundo en la memoria en espera de GC. Eso tiene un gran impacto en Android.
tiguchi
9
¿Quizás uno debería elegir una estructura de datos más adecuada en ese caso? Un conjunto no está diseñado para iterar de manera eficiente. ¿Por qué no usar una ArrayList e iterar sobre ella con un bucle estándar? No se crean iteradores adicionales, el acceso de lectura es muy rápido.
stef77

Respuestas:

492

Puede usar un bucle for mejorado :

Set<String> set = new HashSet<String>();

//populate set

for (String s : set) {
    System.out.println(s);
}

O con Java 8:

set.forEach(System.out::println);
asilias
fuente
65
Creo que esto usa un iterador detrás de escena.
munyengm
22
@munyengm Sí, lo hace. No hay manera de recorrer un conjunto sin un iterador, además de acceder a la estructura subyacente que contiene los datos a través de la reflexión, y replicar el código proporcionado por Set # iterador ...
assylias
1
Esto funciona, pero si podría dar problemas, entonces no es la solución para mí. Supongo que no tengo elección, debo usar Iterator. Gracias por todo de todos modos.
user1621988
39
@ user1621988 ¿Qué problemas? No hay problemas con el código que proporcioné. Simplemente proporciona una manera agradable y limpia de iterar sobre un conjunto sin tener que usar explícitamente un iterador.
Assylias
90

Hay al menos seis formas adicionales de iterar sobre un conjunto. Los siguientes son conocidos por mí:

Método 1

// Obsolete Collection
Enumeration e = new Vector(movies).elements();
while (e.hasMoreElements()) {
  System.out.println(e.nextElement());
}

Método 2

for (String movie : movies) {
  System.out.println(movie);
}

Método 3

String[] movieArray = movies.toArray(new String[movies.size()]);
for (int i = 0; i < movieArray.length; i++) {
  System.out.println(movieArray[i]);
}

Método 4

// Supported in Java 8 and above
movies.stream().forEach((movie) -> {
  System.out.println(movie);
});

Método 5

// Supported in Java 8 and above
movies.stream().forEach(movie -> System.out.println(movie));

Método 6

// Supported in Java 8 and above
movies.stream().forEach(System.out::println);

Este es el HashSetque usé para mis ejemplos:

Set<String> movies = new HashSet<>();
movies.add("Avatar");
movies.add("The Lord of the Rings");
movies.add("Titanic");
Benny Neugebauer
fuente
10
Hola @ benny-neugebauer Para el Método 4 , Método 5 , Método 6 , simplemente puede eliminar redundantes stream().
Eugene T
55
Sin mencionar que el Método 4, el Método 5 y el Método 6 son iguales.
GustavoCinque
24

La conversión de su conjunto en una matriz también puede ayudarlo a iterar sobre los elementos:

Object[] array = set.toArray();

for(int i=0; i<array.length; i++)
   Object o = array[i];
Juvanis
fuente
45
Solo para el registro, toArrayllama al iterador del conjunto.
Assylias
13

Para demostrarlo, considere el siguiente conjunto, que contiene diferentes objetos Persona:

Set<Person> people = new HashSet<Person>();
people.add(new Person("Tharindu", 10));
people.add(new Person("Martin", 20));
people.add(new Person("Fowler", 30));

Clase de modelo de persona

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //TODO - getters,setters ,overridden toString & compareTo methods

}
  1. La declaración for tiene un formulario diseñado para la iteración a través de Colecciones y matrices. A veces se hace referencia a esta forma como la declaración for mejorada, y se puede utilizar para hacer que sus bucles sean más compactos y fáciles de leer.
for(Person p:people){
  System.out.println(p.getName());
}
  1. Java 8 - java.lang.Iterable.forEach (consumidor)
people.forEach(p -> System.out.println(p.getName()));
default void forEach(Consumer<? super T> action)

Performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception. Unless otherwise specified by the implementing class, actions are performed in the order of iteration (if an iteration order is specified). Exceptions thrown by the action are relayed to the caller. Implementation Requirements:

The default implementation behaves as if: 

for (T t : this)
     action.accept(t);

Parameters: action - The action to be performed for each element

Throws: NullPointerException - if the specified action is null

Since: 1.8
Tharindu Rajarathna
fuente
11

Puede usar la operación funcional para un código más ordenado

Set<String> set = new HashSet<String>();

set.forEach((s) -> {
     System.out.println(s);
});
Samuel Moshie
fuente
La llamada requiere API 24
djdance
11

Aquí hay algunos consejos sobre cómo iterar un set junto con sus actuaciones:

public class IterateSet {

    public static void main(String[] args) {

        //example Set
        Set<String> set = new HashSet<>();

        set.add("Jack");
        set.add("John");
        set.add("Joe");
        set.add("Josh");

        long startTime = System.nanoTime();
        long endTime = System.nanoTime();

        //using iterator
        System.out.println("Using Iterator");
        startTime = System.nanoTime();
        Iterator<String> setIterator = set.iterator();
        while(setIterator.hasNext()){
            System.out.println(setIterator.next());
        }
        endTime = System.nanoTime();
        long durationIterator = (endTime - startTime);


        //using lambda
        System.out.println("Using Lambda");
        startTime = System.nanoTime();
        set.forEach((s) -> System.out.println(s));
        endTime = System.nanoTime();
        long durationLambda = (endTime - startTime);


        //using Stream API
        System.out.println("Using Stream API");
        startTime = System.nanoTime();
        set.stream().forEach((s) -> System.out.println(s));
        endTime = System.nanoTime();
        long durationStreamAPI = (endTime - startTime);


        //using Split Iterator (not recommended)
        System.out.println("Using Split Iterator");
        startTime = System.nanoTime();
        Spliterator<String> splitIterator = set.spliterator();
        splitIterator.forEachRemaining((s) -> System.out.println(s));
        endTime = System.nanoTime();
        long durationSplitIterator = (endTime - startTime);


        //time calculations
        System.out.println("Iterator Duration:" + durationIterator);
        System.out.println("Lamda Duration:" + durationLambda);
        System.out.println("Stream API:" + durationStreamAPI);
        System.out.println("Split Iterator:"+ durationSplitIterator);
    }
}

El código se explica por sí mismo.

El resultado de las duraciones son:

Iterator Duration: 495287
Lambda Duration: 50207470
Stream Api:       2427392
Split Iterator:    567294

Podemos ver las Lambdatomas más largas mientras Iteratorque las más rápidas.

Pritam Banerjee
fuente
2

Enumeración(?):

Enumeration e = new Vector(set).elements();
while (e.hasMoreElements())
    {
        System.out.println(e.nextElement());
    }

Otra forma (java.util.Collections.enumeration ()):

for (Enumeration e1 = Collections.enumeration(set); e1.hasMoreElements();)
    {
        System.out.println(e1.nextElement());
    }

Java 8:

set.forEach(element -> System.out.println(element));

o

set.stream().forEach((elem) -> {
    System.out.println(elem);
});
CamelTM
fuente
0

Sin embargo, hay muy buenas respuestas ya disponibles para esto. Aquí está mi respuesta:

1. set.stream().forEach(System.out::println); // It simply uses stream to display set values
2. set.forEach(System.out::println); // It uses Enhanced forEach to display set values

Además, si este conjunto es de tipo de clase personalizada, por ejemplo: Cliente.

Set<Customer> setCust = new HashSet<>();
    Customer c1 = new Customer(1, "Hena", 20);
    Customer c2 = new Customer(2, "Meena", 24);
    Customer c3 = new Customer(3, "Rahul", 30);

setCust.add(c1);
setCust.add(c2);
setCust.add(c3);
    setCust.forEach((k) -> System.out.println(k.getId()+" "+k.getName()+" "+k.getAge()));

// Clase de cliente:

class Customer{
private int id;
private String name;
private int age;

public Customer(int id,String name,int age){
this.id=id;
this.name=name;
this.age=age;
} // Getter, Setter methods are present.}
Garima Garg
fuente