Comment itérer sur un Set / HashSet sans itérateur?

273

Comment puis-je parcourir un Set/ HashSetsans les éléments suivants?

Iterator iter = set.iterator();
while (iter.hasNext()) {
    System.out.println(iter.next());
}
user1621988
la source
31
Essayez-vous réellement d'éviter l'utilisation d'un itérateur, ou ne voulez-vous simplement pas le voir dans votre code source ?
Jon Skeet
2
Vous pouvez rendre le code plus court et plus propre, mais vous devez utiliser un itérateur. Y a-t-il une raison pour laquelle vous voulez l'éviter?
Peter Lawrey
5
Juste pour mémoire et pour expliquer pourquoi éviter l'itérateur: je suis actuellement confronté à ce problème dans un jeu Android écrit en Java. En ce moment, j'utilise un HashSet pour stocker les auditeurs qui doivent être régulièrement informés des événements. Cependant, l'itération répétée des collections provoque une activité importante de ramasse-miettes qui peut surcharger la boucle de jeu. Et c'est un non-go dans un jeu Java. Je vais réécrire ces parties.
tiguchi
12
@thecoshman Je ne parle que du point de vue du développement de jeux Java où vous voulez éviter à tout prix le GC lors des mises à jour régulières de l'état du jeu. Les itérateurs ne sont que des objets temporairement utiles car vous ne pouvez pas les réinitialiser au début, ils sont donc recréés à chaque appel de méthode d'itération (voir la source ArrayList.java par exemple). S'il est utilisé dans une boucle de jeu pour itérer des objets de scène et compte tenu d'au moins 30 mises à jour par seconde, vous vous retrouvez avec au moins 60 (la scène est généralement répétée deux fois par cycle) des objets itérateurs par seconde en mémoire en attente de GC. Cela a un grand impact sur Android.
tiguchi
9
Peut-être faudrait-il choisir une structure de données plus adéquate dans ce cas? Un ensemble n'est pas conçu pour répéter efficacement. Pourquoi ne pas utiliser une ArrayList et itérer dessus avec une boucle for standard? Aucun itérateur supplémentaire n'est créé, l'accès en lecture est très rapide.
stef77

Réponses:

492

Vous pouvez utiliser une boucle for améliorée :

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

//populate set

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

Ou avec Java 8:

set.forEach(System.out::println);
assylias
la source
65
Je crois que cela utilise un itérateur dans les coulisses.
munyengm
22
@munyengm Oui, c'est le cas. Il n'y a aucun moyen d'itérer sur un ensemble sans itérateur, à part accéder à la structure sous-jacente qui contient les données par réflexion et répliquer le code fourni par Set # iterator ...
assylias
1
Cela fonctionne, mais si cela peut poser des problèmes, ce n'est pas la solution pour moi. Je suppose que je n'ai pas le choix, je dois utiliser Iterator. Merci pour tout de toute façon.
user1621988
39
@ user1621988 Quels problèmes? Il n'y a aucun problème avec le code que j'ai fourni. Il fournit simplement un moyen agréable et propre d'itérer sur un ensemble sans avoir à utiliser explicitement un itérateur.
assylias
90

Il existe au moins six façons supplémentaires d'itérer sur un ensemble. Je connais les éléments suivants:

Méthode 1

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

Méthode 2

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

Méthode 3

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

Méthode 4

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

Méthode 5

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

Méthode 6

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

C'est ce HashSetque j'ai utilisé pour mes exemples:

Set<String> movies = new HashSet<>();
movies.add("Avatar");
movies.add("The Lord of the Rings");
movies.add("Titanic");
Benny Neugebauer
la source
10
Salut @ benny-neugebauer Pour la méthode 4 , la méthode 5 , la méthode 6, vous pouvez simplement supprimer la redondance stream().
Eugene T
5
Sans oublier que la méthode 4, la méthode 5 et la méthode 6 sont les mêmes.
GustavoCinque
24

La conversion de votre ensemble en tableau peut également vous aider à parcourir les éléments:

Object[] array = set.toArray();

for(int i=0; i<array.length; i++)
   Object o = array[i];
Juvanis
la source
45
Pour mémoire, toArrayappelle l'itérateur de l'ensemble.
assylias
13

Pour démontrer, considérons l'ensemble suivant, qui contient différents objets Person:

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

Classe de modèle de personne

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. L'instruction for a un formulaire conçu pour l'itération à travers les collections et les tableaux. Ce formulaire est parfois appelé l'instruction for améliorée, et peut être utilisé pour rendre vos boucles plus compactes et plus faciles à lire.
for(Person p:people){
  System.out.println(p.getName());
}
  1. Java 8 - java.lang.Iterable.forEach (consommateur)
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
la source
11

Vous pouvez utiliser une opération fonctionnelle pour un code plus soigné

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

set.forEach((s) -> {
     System.out.println(s);
});
Samuel Moshie
la source
L'appel nécessite l'API 24
djdance
11

Voici quelques conseils sur la façon d'itérer un ensemble ainsi que ses performances:

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

Le code est explicite.

Les résultats des durées sont:

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

Nous pouvons voir que la Lambdaprise est la plus longue et Iteratorla plus rapide.

Pritam Banerjee
la source
2

Énumération(?):

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

Une autre façon (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));

ou

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

Cependant, de très bonnes réponses sont déjà disponibles pour cela. Voici ma réponse:

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

De plus, si cet ensemble est de type Classe personnalisée, par exemple: Client.

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

// Classe de client:

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
la source