How can I iterate over a Set/HashSet without the following?
Iterator iter = set.iterator(); while (iter.hasNext()) { System.out.println(iter.next()); } How can I iterate over a Set/HashSet without the following?
Iterator iter = set.iterator(); while (iter.hasNext()) { System.out.println(iter.next()); } 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); 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"); stream().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]; toArray calls the set's iterator.You can use functional operation for a more neat code
Set<String> set = new HashSet<String>(); set.forEach((s) -> { System.out.println(s); }); 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. ThanksTo 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 } for(Person p:people){ System.out.println(p.getName()); }
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 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.
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); }); 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.} 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.
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)); }