Is there a way of foreaching an Iterator in Java? - java

Java now has a convenient way to foreach a Collection eg.
for (MyClass c : aCollection) {
...
}
Is there something similarly elegant to foreach an Iterator or am I stuck with :
for (;it.hasNext();) {
MyClass c = it.next();
...
}
Update : for people who find this a strange question, I'm used to Python where
for x in xs :
f(x)
works whether xs is a collection OR an iterator (or even a generator). I was kind of caught out and surprised that Java didn't work like this.
I'm implementing a library where I'm returning iterators to some internal collections rather than the collections themselves. But I'm now concerned that this will force the users of my library back to using an older / uglier way of traversing than the foreach. Does this effectively deprecate iterators if people are used to using the foreach construct?

Well, apart from arrays which are a peculiar beast, the "extended for" loop you use here is just a result of the class implementing Iterable. And this interface only provides one method which is to return an... Iterator.
Which means that if you write in code:
for (final Foo foo: someInstanceImplementingIterableOfFoo) {
doSomethingWith(foo);
}
it is in fact equivalent to:
final Iterator<Foo> iterator = someInstanceImplementingIterableOfFoo.iterator();
Foo foo;
while (iterator.hasNext()) {
foo = iterator.next();
doSomethingWith(foo);
}
So, the answer really is no, no elegant way...
That is, unless you use Java 8. It has added forEachRemaining() on Iterator.
Or just create a utility class:
public final class IteratorWrap<T>
implements Iterable<T>
{
private final Iterator<T> iterator;
public static <X> IteratorWrap<X> wrap(final Iterator<X> iterator)
{
return new IteratorWrap<>(iterator);
}
private IteratorWrap(final Iterator<T> iterator)
{
this.iterator = Objects.requireNonNull(iterator);
}
#Override
public Iterator<T> iterator()
{
return iterator;
}
}
In code you'd then just:
for (final Foo foo: IteratorWrap.wrap(iteratorOfX))
doSomethingWith(foo);

Well, since you want something modern, using Java 8 you can do
Iterator<String> names = Arrays.asList("one","two","three").iterator();
for(String name : (Iterable<String>) () -> names) {
System.out.println(name);
}
Since the for-loop expects an Iterable Object, with this technique you can provide an Iterable out of an Iterator by using a lambda expression.
As mentioned by fge in another answer, though, this is equivalent to the following Java 8 expression using method references and avoiding the creation of the Iterable:
names.forEachRemaining(System.out::println);
So, you can pack your logic into a Consumer and solve your problem.

Java's "enhanced" for loop requires that the target to be iterated must implement Iterable. But if you have only an Iterator, which doesn't implement Iterable, there is no built-in way of using the Iterator as the target of a foreach loop in Java.
If you can't use Java 8, you can create an adapter class that wraps the Iterator, implements Iterable, and returns the wrapped iterator in the iterator() method.
class IterableIterator<T> implements Iterable<T>
{
private Iterator<T> iterator;
public IterableIterator(Iterator<T> iterator)
{
this.iterator = iterator;
}
#Override
public Iterator<T> iterator()
{
return iterator;
}
}
Then, if you have an Iterator, you can use it like this:
Iterator<MyClass> itr = getIteratorSomehow();
for (MyClass obj : new IterableIterator<MyClass>(itr))
{
// ...
}

Iterator<String> names = Arrays.asList("one", "two", "three").iterator();
String foo;
while((foo= Iterators.getNext(names, null))!=null){
System.out.println(foo);
}

Related

How can I get to the iterator of an array?

I'd like to use the following class like so:
for(String device : new Devices())
{
//
}
If I provide direct access to the internal string array, then there is no problem:
for(String device : new Devices().getAllDevices()) //getAllDevices would be a String[]
{
//
}
But I just want to forward the iterator, which would be simple if AllDevices were an ArrayList.
public final class Devices implements Iterable<String>{
private static final String MyKindleFire = "123156448975312";
private static final String[] AllDevices = new String[]{MyKindleFire};
#Override
public Iterator<String> iterator() {
// if AllDevices were an array list, this would be possible
// but how should I do this for an array?
return AllDevices.iterator();
}
}
This works, but I'd like to know a better way if possible:
#Override
public Iterator<String> iterator() {
return Arrays.asList(AllDevices).iterator();
}
Unfortunately, you cannot do it without converting your array to List<T>: iterating over arrays with the "foreach" version of the for loop is a "compiler trick", i.e. something the compiler knows and does internally.
An ability to use primitives in "foreach" loops is an indirect indication that Iterator<T> is not used there, because Java generics cannot be used with primitive types.
String[] someArray = ....;
List<String> someList = java.util.Arrays.asList(someArray);
someList.iterator();
I think this is the only way to get an Iterator of an array in pure java.
If you are using apache commons-collections you can simply use:
org.apache.commons.collections.IteratorUtils.arrayIterator(Object[])
See http://commons.apache.org/proper/commons-collections/javadocs/api-release/org/apache/commons/collections/IteratorUtils.html
You could use Guava's Iterators.forArray(T...) to make an iterator.
Alternatively, make an Iterable out of your array (e.g. with Arrays.asList(T...)) and return its .iterator().

Initializing a Set with an Iterable

I want to initialize a Set Implementation (HashSet) in Java with an Iterable. However, the constructor of HashSet doesn't accept Iterables, but only Collections type objects.
Is there a way to convert from Iterable to some subtype of Collections.
You can use Guava.
Set<T> set = Sets.newHashSet(iterable);
or to make it read like a sentence static import,
import static com.google.common.collect.Sets.*;
Set<T> set = newHashSet(iterable);
HashSet constructor relies on more than what Iterable offers: it wants to know the size of the collection up front in order to optimally construct the underlying HashMap. If you have a true, austere Iterable, which doesn't know its size, then you'll have to realize the Iterable up front by turning it into a regular Collection in any of a number of obvious ways.
If, on the other hand, you have a richer object that already knows its size, then it would pay to create a minimalist adapter class that wraps your Iterable into a collection, implementing just size in addition to forwarding the call to iterator.
public class IterableCollection<T> implements Collection<T>
{
private final Iterable<T> iterable;
public IterableCollection(Iterable<T> it) { this.iterable = it; }
#Override public Iterator<T> iterator() { return iterable.iterator(); }
#Override public int size() { return ... custom code to determine size ... }
#Override .... all others ... { throw new UnsupportedOperationException(); }
}
Sure, it's shown in this answer. Basically, iterate over the iterable and copy its contents in a collection:
public static <T> List<T> copyIterable(Iterable<T> iterable) {
Iterator<T> iter = iterable.iterator();
List<T> copy = new ArrayList<T>();
while (iter.hasNext())
copy.add(iter.next());
return copy;
}
Use it as follows, the resulting List object can be passed as a parameter to the HashSet constructor.
Iterable<Integer> list = Arrays.asList(1, 2, 3);
List<Integer> copy = copyIterable(list);
Set<Integer> aSet = new HashSet<Integer>(copy);
EDIT
I've been mistaken all along. Iterable is a superinterface of Collection, so a simple (but unsafe) cast will do the trick, as long as the Iterable was a Collection to begin with.
Iterable<Integer> list = Arrays.asList(1, 2, 3);
Set<Integer> aSet = new HashSet<Integer>((Collection)list); // it works!
The Iterable interface allows the "foreach" syntax to work, so the cleanest way is likely:
public <T> Set<T> toSet(Iterable<T> collection) {
HashSet<T> set = new HashSet<T>();
for (T item: collection)
set.add(item);
return set;
}
Just add each one.
public static <T> Set<T> setFromIterable(Iterable<T> i) {
HashSet<T> set = new HashSet<T>();
Iterator<T> it = i.iterator();
while (it.hasNext()) {
set.add(it.next());
}
return set;
}
Iterable<Integer> someIterable = ...;
Set<Integer> someSet = setFromIterable(someIterable);
Note that you don't use the constructor new HashSet<Integer>(someIterator), because that doesn't exist. Just call the static method.
I use this one-liner (with Java 8+), which only relies on java.util.stream:
StreamSupport.stream(myIterable.spliterator(), false).collect(Collectors.toSet());
// or with static imports:
stream(myIterable.spliterator(), false).collect(toSet());
Putting somewhat a repeated answer for conciseness. Below worked for me for converting the Iterable of String type to a Set(Java8).
Iterable<String> stringIterable = Arrays.asList("str1", "str2", "str3");
Set<String> stringHashSet = new HashSet<>((Collection<? extends String>) stringIterable);

Easy way to convert Iterable to Collection

In my application I use 3rd party library (Spring Data for MongoDB to be exact).
Methods of this library return Iterable<T>, while the rest of my code expects Collection<T>.
Is there any utility method somewhere that will let me quickly convert one to the other? I would like to avoid creating a bunch of foreach loops in my code for such a simple thing.
In JDK 8+, without using any additional libs:
Iterator<T> source = ...;
List<T> target = new ArrayList<>();
source.forEachRemaining(target::add);
Edit: The above one is for Iterator. If you are dealing with Iterable,
iterable.forEach(target::add);
With Guava you can use Lists.newArrayList(Iterable) or Sets.newHashSet(Iterable), among other similar methods. This will of course copy all the elements in to memory. If that isn't acceptable, I think your code that works with these ought to take Iterable rather than Collection. Guava also happens to provide convenient methods for doing things you can do on a Collection using an Iterable (such as Iterables.isEmpty(Iterable) or Iterables.contains(Iterable, Object)), but the performance implications are more obvious.
Concise solution with Java 8 using java.util.stream:
public static <T> List<T> toList(final Iterable<T> iterable) {
return StreamSupport.stream(iterable.spliterator(), false)
.collect(Collectors.toList());
}
Since Java 16, you can use Stream.toList():
public static <T> List<T> toList(final Iterable<T> iterable) {
return StreamSupport.stream(iterable.spliterator(), false)
.toList();
}
You may write your own utility method for this as well:
public static <E> Collection<E> makeCollection(Iterable<E> iter) {
Collection<E> list = new ArrayList<E>();
for (E item : iter) {
list.add(item);
}
return list;
}
IteratorUtils from commons-collections may help (although they don't support generics in the latest stable version 3.2.1):
#SuppressWarnings("unchecked")
Collection<Type> list = IteratorUtils.toList(iterable.iterator());
Version 4.0 (which is in SNAPSHOT at this moment) supports generics and you can get rid of the #SuppressWarnings.
Update: Check IterableAsList from Cactoos.
When you get your Iterable from Spring Data you have a couple of additional alternatives.
You can override the method that returns the Iterable in the repository with a version that returns a List, Set or Streamable. This way Spring Data is doing the conversion for you.
You may do so in a super interface of your repositories so you don't have to repeat the override in all your repository interfaces.
If you happen to use Spring Data JPA this is already done for you in JpaRepository
You may do the conversion using the just mentioned Streamable yourself:
Iterable<X> iterable = repo.findAll();
List<X> list = Streamable.of(iterable).toList();
And since you mention being upset, maybe a little background for the decision to use Iterable help as well.
It is expected that it is actually fairly rare to actually require a Collection so in many cases it shouldn't make a difference.
Using the overriding mechanics one can return different types which wouldn't be possible with a more specific return type like Collection.
This would make it impossible to return a Streamable which is intended for cases where a store may decide to return a result before all elements have been fetched.
Streamable would actually be a flexible return type, since it offers easy conversions to List, Set, Stream and is itself an Iterable. But this would require you to use a Spring Data specific type in your application which many users wouldn't like.
There is a section about this in the reference documentation.
From CollectionUtils:
List<T> targetCollection = new ArrayList<T>();
CollectionUtils.addAll(targetCollection, iterable.iterator())
Here are the full sources of this utility method:
public static <T> void addAll(Collection<T> collection, Iterator<T> iterator) {
while (iterator.hasNext()) {
collection.add(iterator.next());
}
}
I use FluentIterable.from(myIterable).toList() a lot.
I came across a similar situation while trying to fetch a List of Projects, rather than the default Iterable<T> findAll() declared in CrudRepository interface. So, in my ProjectRepository interface (which extends from CrudRepository), I simply declared the findAll() method to return a List<Project> instead of Iterable<Project>.
package com.example.projectmanagement.dao;
import com.example.projectmanagement.entities.Project;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface ProjectRepository extends CrudRepository<Project, Long> {
#Override
List<Project> findAll();
}
This is the simplest solution, I think, without requiring conversion logic or usage of external libraries.
While at it, do not forget that all collections are finite, while Iterable has no promises whatsoever. If something is Iterable you can get an Iterator and that is it.
for (piece : sthIterable){
..........
}
will be expanded to:
Iterator it = sthIterable.iterator();
while (it.hasNext()){
piece = it.next();
..........
}
it.hasNext() is not required to ever return false. Thus in the general case you cannot expect to be able to convert every Iterable to a Collection. For example you can iterate over all positive natural numbers, iterate over something with cycles in it that produces the same results over and over again, etc.
Otherwise: Atrey's answer is quite fine.
This is not an answer to your question but I believe it is the solution to your problem. The interface org.springframework.data.repository.CrudRepository does indeed have methods that return java.lang.Iterable but you should not use this interface. Instead use sub interfaces, in your case org.springframework.data.mongodb.repository.MongoRepository. This interface has methods that return objects of type java.util.List.
I use my custom utility to cast an existing Collection if available.
Main:
public static <T> Collection<T> toCollection(Iterable<T> iterable) {
if (iterable instanceof Collection) {
return (Collection<T>) iterable;
} else {
return Lists.newArrayList(iterable);
}
}
Ideally the above would use ImmutableList, but ImmutableCollection does not allow nulls which may provide undesirable results.
Tests:
#Test
public void testToCollectionAlreadyCollection() {
ArrayList<String> list = Lists.newArrayList(FIRST, MIDDLE, LAST);
assertSame("no need to change, just cast", list, toCollection(list));
}
#Test
public void testIterableToCollection() {
final ArrayList<String> expected = Lists.newArrayList(FIRST, null, MIDDLE, LAST);
Collection<String> collection = toCollection(new Iterable<String>() {
#Override
public Iterator<String> iterator() {
return expected.iterator();
}
});
assertNotSame("a new list must have been created", expected, collection);
assertTrue(expected + " != " + collection, CollectionUtils.isEqualCollection(expected, collection));
}
I implement similar utilities for all subtypes of Collections (Set,List,etc). I'd think these would already be part of Guava, but I haven't found it.
As soon as you call contains, containsAll, equals, hashCode, remove, retainAll, size or toArray, you'd have to traverse the elements anyway.
If you're occasionally only calling methods such as isEmpty or clear I suppose you'd be better of by creating the collection lazily. You could for instance have a backing ArrayList for storing previously iterated elements.
I don't know of any such class in any library, but it should be a fairly simple exercise to write up.
In Java 8 you can do this to add all elements from an Iterable to Collection and return it:
public static <T> Collection<T> iterableToCollection(Iterable<T> iterable) {
Collection<T> collection = new ArrayList<>();
iterable.forEach(collection::add);
return collection;
}
Inspired by #Afreys answer.
Since RxJava is a hammer and this kinda looks like a nail, you can do
Observable.from(iterable).toList().toBlocking().single();
Here's an SSCCE for a great way to do this in Java 8
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class IterableToCollection {
public interface CollectionFactory <T, U extends Collection<T>> {
U createCollection();
}
public static <T, U extends Collection<T>> U collect(Iterable<T> iterable, CollectionFactory<T, U> factory) {
U collection = factory.createCollection();
iterable.forEach(collection::add);
return collection;
}
public static void main(String[] args) {
Iterable<Integer> iterable = IntStream.range(0, 5).boxed().collect(Collectors.toList());
ArrayList<Integer> arrayList = collect(iterable, ArrayList::new);
HashSet<Integer> hashSet = collect(iterable, HashSet::new);
LinkedList<Integer> linkedList = collect(iterable, LinkedList::new);
}
}
Two remarks
There is no need to convert Iterable to Collection to use foreach
loop - Iterable may be used in such loop directly, there is no
syntactical difference, so I hardly understand why the original question was asked at all.
Suggested way to convert Iterable to Collection is unsafe (the same relates to CollectionUtils) - there is no guarantee that subsequent calls to the next() method return different object instances. Moreover, this concern is not pure theoretical. E.g. Iterable implementation used to pass values to a reduce method of Hadoop Reducer always returns the same value instance, just with different field values. So if you apply makeCollection from above (or CollectionUtils.addAll(Iterator)) you will end up with a collection with all identical elements.
Try StickyList from Cactoos:
List<String> list = new StickyList<>(iterable);
Kinda late to the party, but I created a very elegant Java 8 solution that allows converting an Iterable of T to any Collection of T, without any libraries:
public static <T, C extends Collection<T>> C toCollection(Iterable<T> iterable, Supplier<C> baseSupplier)
{
C collection = baseSupplier.get();
iterable.forEach(collection::add);
return collection;
}
Usage Example:
Iterable<String> iterable = ...;
List<String> list = toCollection(iterable, ArrayList::new);
You can use Eclipse Collections factories:
Iterable<String> iterable = Arrays.asList("1", "2", "3");
MutableList<String> list = Lists.mutable.withAll(iterable);
MutableSet<String> set = Sets.mutable.withAll(iterable);
MutableSortedSet<String> sortedSet = SortedSets.mutable.withAll(iterable);
MutableBag<String> bag = Bags.mutable.withAll(iterable);
MutableSortedBag<String> sortedBag = SortedBags.mutable.withAll(iterable);
You can also convert the Iterable to a LazyIterable and use the converter methods or any of the other available APIs available.
Iterable<String> iterable = Arrays.asList("1", "2", "3");
LazyIterable<String> lazy = LazyIterate.adapt(iterable);
MutableList<String> list = lazy.toList();
MutableSet<String> set = lazy.toSet();
MutableSortedSet<String> sortedSet = lazy.toSortedSet();
MutableBag<String> bag = lazy.toBag();
MutableSortedBag<String> sortedBag = lazy.toSortedBag();
All of the above Mutable types extend java.util.Collection.
Note: I am a committer for Eclipse Collections.
If you could update to Spring Data 3, this has been addressed there. There is a new interface ListCrudRepository which do exactly what you want.
Here is the interface from https://spring.io/blog/2022/02/22/announcing-listcrudrepository-friends-for-spring-data-3-0:
public interface ListCrudRepository<T, ID> extends CrudRepository<T, ID> {
<S extends T> List<S> saveAll(Iterable<S> entities);
List<T> findAll();
List<T> findAllById(Iterable<ID> ids);
}
Note in version 3 you must implement two interfaces
So in version 2:
public interface PersonRepository<Person, Long> extends
PagingAndSortingRepository<Person, Long> {}
In version 3 should be changed to:
public interface PersonRepository<Person, Long> extends
PagingAndSortingRepository<Person, Long>,ListCrudRepository<Person, Long> {}
Other changes are mentioned in https://spring.io/blog/2022/02/22/announcing-listcrudrepository-friends-for-spring-data-3-0

Java List.add() UnsupportedOperationException

I try to add objects to a List<String> instance but it throws an UnsupportedOperationException.
Does anyone know why?
My Java code:
String[] membersArray = request.getParameterValues('members');
List<String> membersList = Arrays.asList(membersArray);
for (String member : membersList) {
Person person = Dao.findByName(member);
List<String> seeAlso;
seeAlso = person.getSeeAlso();
if (!seeAlso.contains(groupDn)){
seeAlso.add(groupDn);
person.setSeeAlso(seeAlso);
}
}
The error message:
java.lang.UnsupportedOperationException
java.util.AbstractList.add(Unknown Source)
java.util.AbstractList.add(Unknown Source)
javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
Not every List implementation supports the add() method.
One common example is the List returned by Arrays.asList(): it is documented not to support any structural modification (i.e. removing or adding elements) (emphasis mine):
Returns a fixed-size list backed by the specified array.
Even if that's not the specific List you're trying to modify, the answer still applies to other List implementations that are either immutable or only allow some selected changes.
You can find out about this by reading the documentation of UnsupportedOperationException and List.add(), which documents this to be an "(optional operation)". The precise meaning of this phrase is explained at the top of the List documentation.
As a workaround you can create a copy of the list to a known-modifiable implementation like ArrayList:
seeAlso = new ArrayList<>(seeAlso);
Many of the List implementation support limited support to add/remove, and Arrays.asList(membersArray) is one of that. You need to insert the record in java.util.ArrayList or use the below approach to convert into ArrayList.
With the minimal change in your code, you can do below to convert a list to ArrayList. The first solution is having a minimum change in your solution, but the second one is more optimized, I guess.
String[] membersArray = request.getParameterValues('members');
ArrayList<String> membersList = new ArrayList<>(Arrays.asList(membersArray));
OR
String[] membersArray = request.getParameterValues('members');
ArrayList<String> membersList = Stream.of(membersArray).collect(Collectors.toCollection(ArrayList::new));
Form the Inheritance concept, If some perticular method is not available in the current class it will search for that method in super classes. If available it executes.
It executes AbstractList<E> class add() method which throws UnsupportedOperationException.
When you are converting from an Array to a Collection Obejct. i.e., array-based to collection-based API then it is going to provide you fixed-size collection object, because Array's behaviour is of Fixed size.
java.util.Arrays.asList( T... a )
Souce samples for conformation.
public class Arrays {
public static <T> List<T> asList(T... a) {
return new java.util.Arrays.ArrayList.ArrayList<>(a); // Arrays Inner Class ArrayList
}
//...
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable {
//...
}
}
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
//...
}
public ListIterator<E> listIterator() {
return listIterator(0);
}
private class ListItr extends Itr implements ListIterator<E> {
//...
}
}
Form the above Source you may observe that java.util.Arrays.ArrayList class doesn't #Override add(index, element), set(index, element), remove(index). So, From inheritance it executes super AbstractList<E> class add() function which throws UnsupportedOperationException.
As AbstractList<E> is an abstract class it provides the implementation to iterator() and listIterator(). So, that we can iterate over the list object.
List<String> list_of_Arrays = Arrays.asList(new String[] { "a", "b" ,"c"});
try {
list_of_Arrays.add("Yashwanth.M");
} catch(java.lang.UnsupportedOperationException e) {
System.out.println("List Interface executes AbstractList add() fucntion which throws UnsupportedOperationException.");
}
System.out.println("Arrays → List : " + list_of_Arrays);
Iterator<String> iterator = list_of_Arrays.iterator();
while (iterator.hasNext()) System.out.println("Iteration : " + iterator.next() );
ListIterator<String> listIterator = list_of_Arrays.listIterator();
while (listIterator.hasNext()) System.out.println("Forward iteration : " + listIterator.next() );
while(listIterator.hasPrevious()) System.out.println("Backward iteration : " + listIterator.previous());
You can even create Fixed-Size array form Collections class Collections.unmodifiableList(list);
Sample Source:
public class Collections {
public static <T> List<T> unmodifiableList(List<? extends T> list) {
return (list instanceof RandomAccess ?
new UnmodifiableRandomAccessList<>(list) :
new UnmodifiableList<>(list));
}
}
A Collection — sometimes called a container — is simply an object that groups multiple elements into a single unit. Collections are used to store, retrieve, manipulate, and communicate aggregate data.
#see also
HashMap vs HashTable
Object Serialization uses the Serializable and Externalizable interfaces
You will also get this exception if you attempt to add to a List<T> returned by Collections.singletonList(T o):
Returns an immutable list containing only the specified object. The returned list is serializable.
The JVM does not implement add() for Collections.singletonList
List membersList = Arrays.asList(membersArray);
returns immutable list, what you need to do is
new ArrayList<>(Arrays.asList(membersArray)); to make it mutable
You must initialize your List seeAlso :
List<String> seeAlso = new Vector<String>();
or
List<String> seeAlso = new ArrayList<String>();
You cannot modify a result from a LDAP query. Your problem is in this line:
seeAlso.add(groupDn);
The seeAlso list is unmodifiable.
instead of using add() we can use addall()
{ seeAlso.addall(groupDn); }
add adds a single item, while addAll adds each item from the collection one by one. In the end, both methods return true if the collection has been modified. In case of ArrayList this is trivial, because the collection is always modified, but other collections, such as Set, may return false if items being added are already there.

Can we use for-each loop for iterating the objects of Iterator type? [duplicate]

This question already has answers here:
Idiomatic way to use for-each loop given an iterator?
(9 answers)
Closed 3 years ago.
If we do the following we get error:
class FGH{
public static Iterator reverse(List list) {
Collections.reverse(list);
return list.iterator();
}
public static void main(String[] args) {
List list = new ArrayList();
list.add("1"); list.add("2"); list.add("3");
/*for(Iterator it:reverse(list))
Iterator it=reverse(list);*/
for (Object obj: reverse(list))
System.out.print(obj + ", ");}}
but if we modify the code like this we don't get error,so is it mean that we can't iterate the objects of Iterator type? :
class FGH{
public static Iterator reverse(List list) {
Collections.reverse(list);
return list.iterator();
}
public static void main(String[] args) {
List list = new ArrayList();
list.add("1"); list.add("2"); list.add("3");
Iterator it=reverse(list);
while(it.hasNext()){
Object obj=it.next();
System.out.println(obj);
}
}}
A lot of answers talk about how iterators are not iterables. That's true, but such an answer doesn't touch on why.
The reason for-each loops require an iterable is to allow the same object to be traversed multiple times (so that you can use multiple for-each loops over the same object without surprising behaviour), whereas an iterator only allows one traversal. If for-each allowed iterators to be used, the behaviour would be surprising to programmers who didn't realise that their iterators would be exhausted after the loop is run.
If you're using an API that only gives you iterators, and you want to use iterables, you have two ways to solve this:
Make an anonymous iterable class, whose iterator() method calls the API function that returns the iterator. That way, each time you use a for-each loop on the iterable object, that API function is called again, returning a new (and unexhausted) iterator.
Make a one-pass iterable wrapper class that takes an iterator and allows one call to iterator(). On subsequent calls, throw an AssertionError or IllegalStateException as appropriate.
The for loop in your first example expects that reverse(list) is a collection of Iterator, which of course it isn't. That's why that example won't work.
In general, you can only use foreach on classes that implement Iterable. Iterator is not one of these classes.
See http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Iterable.html
You would have to wrap your Iterator in an Iterable first. Here's a Java 8 solution:
for (Object obj : (Iterable<Object>) () -> reverse(list))
System.out.print(obj + ", ");
Or, just use Iterator.forEachRemaining():
reverse(list).forEachRemaining(obj -> System.out.print(obj + ", "));
You'd use the enhanced for loop in this way:
for (Object o : list)
As pointed out above, you need to reference an Iterable (or array) in a for-each block. For example, when you do the following:
Collection<String> coll = ...;
for (String str : coll) {
...
}
The following is really happening:
Collection<String> coll = ...;
for (Iterator<String> iter = coll.iterator(); iter.hasNext(); ) {
String str = iter.next();
}
In order to be able to do this, the argument has to implement Iterable (e.g. have a public iterator() method that returns an Iterator (or be an array). So your code should look like the following:
class FGH {
public static void main(String[] args) {
List list = new ArrayList();
list.add("1"); list.add("2"); list.add("3");
while (Object obj : Collections.reverse(list)){
System.out.println(obj);
}
}
}
The "for each" syntax is designed for collections (Iterable to be precise), not for Iterators. I'm trying to find out the arguments of why Java was desinged this way in this question.
The simplest solution for you would be to return the reversed list (which is iterable) instead of a list iterator. Then you can use the shorthand for loop syntax.
A hackish way is outlined in the question mentioned earlier, using an Adapter class that wraps the Iterator as Iterable, and returns the Iterator on the first invocation of iterator(). Look at the adapter code, it's fairly simple. It serves the purpose, but since the iterator can only be used once, it is somewhat an invalid Iterable (it will not be able produce a second iterator).
The key behavior difference shows up when you use it twice:
for(Object a : foo) { }
for(Object a : foo) { }
will process all elements in foo twice if it is a proper Iterable, but only once if it is using the adapter I sketched - the second loop will do nothing.

Categories

Resources