¿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());
}

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);

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

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];

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

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);
});

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.


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);
});

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.}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.