336

How can I iterate over a Set/HashSet without the following?

Iterator iter = set.iterator(); while (iter.hasNext()) { System.out.println(iter.next()); } 
9
  • 36
    Are you actually trying to avoid the use of an iterator, or do you just not want to see it in your source code? Commented Sep 17, 2012 at 8:46
  • 2
    You can make the code shorter and cleaner, but you have to use an Iterator. Is there a reason you want to avoid it? Commented Sep 17, 2012 at 8:46
  • 5
    Just for the record and as explanation of why to avoid the iterator: I'm currently facing that issue in an Android game written in Java. Right now I'm using a HashSet for storing listeners that need to be regularly notified about events. However, repeatedly iterating collections causes heavy garbage collector activity that can burden the game loop. And that's a no-go in a Java game. I'm going to rewrite these parts. Commented Jan 28, 2013 at 19:36
  • 12
    @thecoshman I'm talking only from a Java game development perspective where you want to avoid GC during regular game state updates at all costs. Iterators are only temporarily useful objects since you cannot reset them to the start, therefore they get recreated on each iteration method call (see ArrayList.java source for example). If used in a game loop for iterating scene objects and considering at least 30 updates per second you end up with at least 60 (scene is usually iterated twice per cycle) iterator objects per second in memory waiting for GC. That has a big impact on Android. Commented Jan 27, 2014 at 17:40
  • 9
    Perhaps one should pick a more adequate data structure in that case? A set's not designed to iterate over efficiently. Why not use an ArrayList and iterate over it with a standard for loop? No additional iterators are created, read access is very fast. Commented Jul 23, 2015 at 7:45

10 Answers 10

581

You can use an enhanced for loop:

Set<String> set = new HashSet<String>(); //populate set for (String s : set) { System.out.println(s); } 

Or with Java 8:

set.forEach(System.out::println); 
Sign up to request clarification or add additional context in comments.

5 Comments

I believe this uses an iterator behind the scenes.
@munyengm Yes it does. There is no way to iterate over a set without an iterator, apart from accessing the underlying structure that holds the data through reflection, and replicating the code provided by Set#iterator...
This works, but if it could gives issues then it's not the solution for me. I guess i have no choose, i must use Iterator. Thanks for all anyway.
@user1621988 What issues? There are no issues with the code I provided. It just provides a nice and clean way to iterate over a set without having to explicitly use an iterator.
@assylias This could lead to confusion as title clearly states that looping over elements without iterator. You could add a little bit of caution here that it internally uses iterator only so the solution only provides syntactic solution not the actual one
118

There are at least six additional ways to iterate over a set. The following are known to me:

Method 1

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

Method 2

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

Method 3

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

Method 4

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

Method 5

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

Method 6

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

This is the HashSet which I used for my examples:

Set<String> movies = new HashSet<>(); movies.add("Avatar"); movies.add("The Lord of the Rings"); movies.add("Titanic"); 

2 Comments

Hi @benny-neugebauer For Method 4, Method 5, Method 6 you can simply remove redundant stream().
Not to mention that Method 4, Method 5 and Method 6 are the same.
25

Converting your set into an array may also help you for iterating over the elements:

Object[] array = set.toArray(); for(int i=0; i<array.length; i++) Object o = array[i]; 

1 Comment

Just for the record, toArray calls the set's iterator.
14

You can use functional operation for a more neat code

Set<String> set = new HashSet<String>(); set.forEach((s) -> { System.out.println(s); }); 

2 Comments

Call requires API 24
the forEach you have mentioned is same as set.forEach(System.out::println); as we are dealing with lamda expression/function interface which means interface with only 1 method, so just pass the method reference that is it. Thanks
13

To demonstrate, consider the following set, which holds different Person objects:

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

Person Model Class

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. The for statement has a form designed for iteration through Collections and arrays .This form is sometimes referred to as the enhanced for statement, and can be used to make your loops more compact and easy to read.
for(Person p:people){ System.out.println(p.getName()); } 
  1. Java 8 - java.lang.Iterable.forEach(Consumer)
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 

Comments

11

Here are few tips on how to iterate a Set along with their 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); } } 

The code is self explanatory.

The result of the durations are:

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

We can see the Lambda takes the longest while Iterator is the fastest.

Comments

2

Enumeration(?):

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

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

or

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

Comments

1

However there are very good answers already available for this. Here is my answer:

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 

Also, if this Set is of Custom class type, for eg: Customer.

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

// Customer class:

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.} 

Comments

1

The "enhanced for loop" is the simplest, assuming you just don't want to do explicit iteration yourself.

If on the other hand you're trying to avoid creating any new object to handle the "walk" of the elements, then there's some underlying reason why you want this: it's not a meaningful goal without more context.

The only reason hinted at in the comments I've seen so far is performance.

repeatedly iterating collections causes heavy garbage collector activity

What if you want a collection without duplicates that can add in O(1) and yet you need to iterate on it often?

However, any performance concerns around use of an iterator (including GC performance, i.e. disposing of each used iterator object) must be weighed against the performance of the app's other actions, e.g., how often will add be called relative to other collection methods? what about the expected size(s) of the set? is concurrency a factor, i.e., should the set be modifiable during processing? what are the specific methods being called on the elements? do they need to return a value? can they be parallelized? etc.

Theoretical O()-complexity measures are not as important than actual performance under your real-world input. It might be that a LinkedHashSet or even TreeSet would give better performance. It might be that using .parallelStream().map would be useful. You might benefit from a different data type for the elements themselves, or a different algorithm for executing the underlying logic. But there's no single answer to "what would give better performance than a for loop.

Comments

0

I want to elaborate some theoretical aspects behind the choices we have for iterating over a Collection, which I believe will help you understand this in a more generic way.

(Read more on the traversing a collection here, But, Keep in mind that these Java Tutorials were written for Java 8 https://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html)


The Set interface contains only methods inherited from Collection and adds the restriction that duplicate elements are prohibited https://docs.oracle.com/javase/tutorial/collections/interfaces/set.html

So, the Set interface supports all the traversing ways the Collection interface supports. Following are the traversing options you have with any Collection.

1. Use a stream and perform aggregate operations on it

In JDK 8 and later, the Collection interface also exposes methods Stream stream() and Stream parallelStream(), for obtaining sequential or parallel streams from the underlying collection https://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html

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

Keep in mind that forEach is just one of the aggregate operations.There are other aggregate operations we can use on streams. We can combine aggregator operations to create pipelines. Read more on here: https://docs.oracle.com/javase/tutorial/collections/streams/index.html

2. Use an iterator

Iterator<String> iter = set.iterator(); while (iter.hasNext()) { System.out.println(iter.next()); } 

( As per your question you want to avoid using an iterator. So this will not be an option for your case )

3. Use the for-each construct

If you want to avoid the iterator maybe because it clutters your code this might be a good alternative. But it internally uses an iterator but hides the iterator from developers (read more on this: https://docs.oracle.com/javase/8/docs/technotes/guides/language/foreach.html)

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

4. Use the forEach default method from Iterable interface

Additionally, starting from Java 8, we have a new default method in the Iterable interface (https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/Iterable.java):

public interface Collection<E> extends Iterable<E> { // ... } public interface Iterable<T> { //... default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } // ... } 

As you can see, it internally uses the for-each construct, which is again an iterator abstraction. This was introduced to facilitate functional programming with Java 8.


Convert to an Array and traverse

Apart from that, in the Collection API, you can find the toArray methods, which are provided as a bridge between Collections and arrays. You can use this to convert your Collection to an array and iterate if you really need to:

Object[] array = set.toArray(); for (Object element : array) { System.out.println(element); } 

Convert to a List and iterate

Beyond all the above, I can think of another approach by utilizing the List interface. This is not the optimal way obviously, It's just another way.

Apart the basic Collection operations, List supports positional access like get(int i), add(int i), remove(int i), and so on. These additional implementations enable List to use a classic for loop.

We can leverage that by converting the Set to a List first and then traversing it. we can do the conversion easily because, by convention, all general-purpose Collection implementations have a constructor that takes a Collection argument.

List<String> list = new ArrayList<>(set); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.