Iterator vs For-Each - java

I know there are a lot of similar questions here on SO. But it seems this wasn't discussed before.
In general I want to know, why an Iterator should be used over a For-Each loop, if no element will be removed? So, basically just iterating over a Collection, and the only used modifier is add.
I'm aware that Iterator is the only save option if an element is removed from a Collection.
Performance is no argument, because the For-Each will be transformed into an Iterator by the compiler.
The Iterator is more verbose, so +1 to For-Each.
Is there any reliable point to use an Iterator over For-Each in the described scenario?

You need to use an Iterator loop instead of a foreach loop if
you need to be able to remove items during processing, OR
you need to process the last item differently than the others.
Also, an Iterator may be more natural if you want to be able to skip an element based on characteristics of its previous element.
Additionally, for Lists, a ListIterator loop may sometimes be more convenient than a foreach loop, as it can provide the indexes of the previous and next elements, and (even apart from the indexes) allows you to recognize the first element of the iteration. Furthermore, you need a ListIterator if
you have to replace elements in the underlying List, OR
you ever need to back up.

Related

Why not use ListIterator for full LinkedList Operation?

My main question is if ListIterator or Iterator class reduces the time taken for removal of the elements from a given LinkedList and the same can be said while adding elements in the same given LinkedList using any one of the following classes above. What's the point of using the inbuilt functions of LinkedList class itself? Why should we perform any of the operations through LinkedList functions when we can use the ListIterator functions for better performance?
A ListIterator can indeed efficiently remove the node on which it is positioned. You can thus create a ListIterator, use next() two times to move the cursor, and then remove the node instantly. But evidently you did a lot of work before the actual removal.
Using ListIterator.remove is not more efficient "time complexity"-wise than removing through the LinkedList.remove(int index) if you need to construct the iterator. The LinkedList.remove method takes O(k) time, with k the index of the item you wish to remove. Removing this element with the ListIterator has the same timecomplexity since: (a) we create a ListIterator in constant time; (b) we call .next() k times, each operation in O(1); and (c) we call .remove() which is again O(1). But since we call .next() k times, this is thus an O(k) operation as well.
A similar situation happens for .add(..) on an arbitrary location (an "insert"), except that we here of course insert a node, not remove one.
Now since the two have the same time complexity, one might wonder why a LinkedList has such remove(int index) objects in the first place. The main reason is programmer's convenience. It is more convenient to call mylist.remove(5), than to create an iterator, use a loop to move five places, and then call remove. Furthermore the methods on a linked list guard against some edge-cases like a negative index, etc. By doing this manually you might end removing the first element, which might not be the intended behaviour. Finally code written is sometimes read multiple times. If a future reader reads mylist.remove(5), they understand that it removes the fifth element, wheres a solution with looping will require some extra brain cycles to understand what that part is doing.
As #Andreas says, furthermore the List interface defines these methods, and hence the LinkedList<T> should implement these.

Enhanced for-loop does not accept Iterator

Excuse me if this has been asked before. My search did not bring up any other similar question. This is something that surprised me in Java.
Apparently, the enhanced for-loop only accepts an array or an instance of java.lang.Iterable. It does not accept a java.util.Iterator as a valid obj reference to iterate over. For example, Eclipse shows an error message for the following code. It says: "Can only iterate over an array or an instance of java.lang.Iterable"
Set<String> mySet = new HashSet<String>();
mySet.add("dummy");
mySet.add("test");
Iterator<String> strings = mySet.iterator();
for (String str : strings) {
// Process str value ...
}
Why would this be so? I want to know why the enhanced for-loop was designed to work this way. Although an Iterator is not a collection, it can be used to return one element at a time from a collection. Note that the only method in the java.lang.Iterable interface is Iterator<T> iterator() which returns an Iterator. Here, I am directly handing it an Iterator. I know that hasNext() and next() can be used but using the enhanced for-loop makes it look cleaner.
One thing I understand now is that I could use the enhanced for-loop directly over mySet. So I don't even need the extra call to get an Iterator. So, that would be the way to code this, and yes - it does make some sense.
The enhanced for loop was part of JSR 201. From that page, you can download the proposed final draft documents, which include a FAQ section directly addressing your question:
Appendix I. Design FAQ
Why can't I use the enhanced for statement with an Iterator (rather
than an Iterable or array)?
Two reasons: (1) The construct would not
provide much in the way on syntactic improvement if you had an
explicit iterator in your code, and (2) Execution of the loop would
have the "side effect" of advancing (and typically exhausting) the
iterator. In other words, the enhanced for statement provides a
simple, elegant, solution for the common case of iterating over a
collection or array, and does not attempt to address more complicated
cases, which are better addressed with the traditional for statement.
Why can't I use the enhanced for statement to:
remove elements as I traverse a collection ("filtering")?
simultaneously iterate over multiple collections or arrays?
modify the current slot in an array or list?
See Item 1 above. The expert group considered these cases, but
opted for a simple, clean extension that dose(sic) one thing well. The
design obeys the "80-20 rule" (it handles 80% of the cases with 20% of
the effort). If a case falls into the other 20%, you can always use an
explicit iterator or index as you've done in the past.
In other words, the committee chose to limit the scope of the proposal, and some features that one could imagine being part of the proposal didn't make the cut.
The enhanced for loop was introduced in Java 5 as a simpler way to
iterate through all the elements of a Collection [or an array].
http://www.cis.upenn.edu/~matuszek/General/JavaSyntax/enhanced-for-loops.html
An iterator is not a collection of elements,
it is an object that enables a programmer to traverse a container.
An iterator may be thought of as a type of pointer.
https://en.wikipedia.org/wiki/Iterator
So enhanced for loops work by going through all the elements in a structure that contains elements, while an iterator doesn't contain elements, it acts more like a pointer.
In your example, you are creating an iterator but not using it properly. As to answer your question of why the exception is being thrown- it's from the line:
for (String str : strings) {
"strings" is an iterator here, not a collection that you can iterate through. So you have a few options you can iterate through the set by using an enhanced for loop:
for(String myString : mySet){
//do work
}
or you can iterate through the set using an iterator:
Iterator<String> strings = mySet.iterator();
while(strings.hasNext()){
//do work
}
hope you find this helpful.
The error comes because you are trying to iterate over an Iterator, and not a List or Collection. If you want to use the Iterator, i recommend you to use it next() and hasNext() methods:
Set<String> mySet = new HashSet<String>();
mySet.add("dummy");
mySet.add("test");
Iterator<String> strings = mySet.iterator();
while(strings.hasNext()){
String temp = strings.next();
System.out.println(temp);
}

Removing elements from ListBuffer

According to this posting, it is said that ListBuffer allows constant-time removal of the first and last elements. I've been looking into the API reference and the ListBuffer source code, but I can't find how I remove the last element in constant time while remove(0) will do the job for the first element. What would be the proper way to remove the last element?
Another question: is it possible to remove an element efficiently while iterating over a ListBuffer? In Java it can be done with Iterator.remove() but the Scala iterator doesn't seem to have the remove() method...
The first question has an easy if disappointing answer: you can't remove the last element in constant time, as doing so would require a reference to the element-before-last. (It's a singly linked list, inside a wrapper class that holds the beginning and end elements of the list.)
The second question is equally easy and perhaps disappointing: Iterators in Scala are simply views of the collection. They don't modify the underlying collection. (This is in keeping with the "immutable by default, mutable only when necessary" philosophy.)
You can remove the last element with trimEnd(1)

Seeking further understanding on Iterators in java

If I am using a for loop (the standard for loop, not an enhanced for statement), I fail to see how an iterator increases efficiency when searching through a collection. If I have a statement such as:
(Assuming that aList is a List of generic objects, type E, nextElement refers to the next element within the list)
for (int index = 0; index < aList.size(); index++){
E nextElement = aList.get(index);
// do something with nextElement...
}
and I have the get method that looks something like:
Node<E> nodeRef = head;
for (int i = 0; i < index; i++){
nodeRef = nodeRef.next;
// possible other code
}
this would essentially be searching through the List, one element at a time. However, if I use an iterator, will it not be doing the same operation? I know an iterator is supposed to be O(1) speed, but wouldn't it be O(n) if it has to search through the entire list anyway?
It's not primarily about efficiency, IMO. It's about abstraction. Using an index ties you to collections which can retrieve an item for a given index efficiently (so it won't work well with a linked list, say)... and it doesn't express what you're trying to do, which is iterate over the list.
With an iterator, you can express the idea of iterating over a sequence of items whether that sequence can easily be indexed or not, whether the size is known in advance or not, and even in cases where it's effectively infinite.
Your second case is still written using a for loop which increments an index, which isn't the idiomatic way of thinking about it - it should simply be testing whether or not it's reached the end. For example, it might be:
for (Node<E> nodeRef = head; nodeRef != null; nodeRef = nodeRef.next)
{
}
Now we have the right abstraction: the loop expresses where we start (the head), when we stop (when there are no more elements) and how we go from one element to the next (using the next field). This expresses the idea of iterating more effectively than "I've got a counter starting at 0, and I'm going to ask for the value at the particular counter on each iteration until the value of the counter is greater than some value which happens to be the length of the list."
We're fairly used to the latter way of expressing things, but it doesn't really say what we mean nearly as may as the iterator approach.
Iterators are not about increasing efficiency, they're about abstraction in the object-oriented sense. Implementation-wise, the iterator is doing something similar to what you're doing, going through your collection one element at a time, at least if the collection is index-based. It's supposed to be O(1) when retrieving the next element, not the entire list. Iterators help mask what collection is underneath as well, it could be a linked list or a set, etc, but you don't have to know.
Also, notice how connected your for loop is to your specific logic that you want to do on each element, while with an iterator you can abstract out the looping logic from whatever action you want to do.
I think the question you are asking refers to the efficiency of iterators vs. a for-loop using an explicit get on the collection object.
If you write code with a naive version of get, and you iterate through your list using it, then it takes you
one step to "get" the first element
two steps to "get" the second
three steps to get the third
...
n steps to get the last
for a total of n(n-1)/2 operations, which is O(n^2).
But if you used an iterator which internally kept track of the next element (i.e. one step to advance), then iterating the whole list is O(n), a big improvement.
Like Jon said, iterators have nothing to do with efficiency they just abstract the concept of being able to iterate over a collection. So you are right, if you are just searching through a list there is no real benefit to an iterator over a for loop, but in some cases iterators provide convenient ways for doing things that would be difficult with a simple for loop. For example:
while(itr.hasNext()) {
if(itr.next().equals(somethingBad);
itr.remove();
}
In other cases iterators provide a way to traverse the elements of a collection, that you can not fetch by index (eg a hashset). In this case a for loop is not an option.
Remember that it's also a Design Pattern.
"The Iterator Pattern allows traversal of the elements of an aggregate without exposing the underlying implementation. It also places the task of traversal on the iterator object, not on the aggregate, which simplifies the aggregate interface and implementation, and places the responsibility where it should be." (From: Head First Design Pattern)
It's about encapsulation and also the 'single responsibility' principle.
Cheers,
Wim
You are using a linked list here. Iterating over that list without an iterator takes O(n^2) steps, where n is the size of the list. O(n) for iterating over the list and O(n) each time for finding the next element.
The iterator, on the other hand, remembers the node it has visited the last time, and therefore needs only O(1) to find the next element. So eventually the complexity is O(n), which is faster.

Whats the replacement of For-Each loop for filtering?

Though for-each loop has many advantages but the problem is ,it doesn't work when you want to Filter(Filtering means removing element from List) a List,Can you please any replacement as even traversing through Index is not a good option..
What do you mean by "filtering"? Removing certain elements from a list? If so, you can use an iterator:
for(Iterator<MyElement> it = list.iterator(); it.hasNext(); ) {
MyElement element = it.next();
if (some condition) {
it.remove();
}
}
Update (based on comments):
Consider the following example to illustrate how iterator works. Let's say we have a list that contains 'A's and 'B's:
A A B B A
We want to remove all those pesky Bs. So, using the above loop, the code will work as follows:
hasNext()? Yes. next(). element points to 1st A.
hasNext()? Yes. next(). element points to 2nd A.
hasNext()? Yes. next(). element points to 1st B. remove(). iterator counter does NOT change, it still points to a place where B was (technically that's not entirely correct but logically that's how it works). If you were to call remove() again now, you'd get an exception (because list element is no longer there).
hasNext()? Yes. next(). element points to 2nd B. The rest is the same as #3
hasNext()? Yes. next(). element points to 3rd A.
hasNext()? No, we're done. List now has 3 elements.
Update #2: remove() operation is indeed optional on iterator - but only because it is optional on an underlying collection. The bottom line here is - if your collection supports it (and all collections in Java Collection Framework do), so will the iterator. If your collection doesn't support it, you're out of luck anyway.
ChssPly76's answer is the right approach here - but I'm intrigued as to your thinking behind "traversing through index is not a good option". In many cases - the common case in particular being that of an ArrayList - it's extremely efficient. (In fact, in the arraylist case, I believe that repeated calls to get(i++) are marginally faster than using an Iterator, though nowhere near enough to sacrifice readability).
Broadly speaking, if the object in question implements java.util.RandomAccess, then accessing sequential elements via an index should be roughly the same speed as using an Iterator. If it doesn't (e.g. LinkedList would be a good counterexample) then you're right; but don't dismiss the option out of hand.
I have had success using the
filter(java.util.Collection collection, Predicate predicate)
method of CollectionUtils in commons collections.
http://commons.apache.org/collections/api-2.1.1/org/apache/commons/collections/CollectionUtils.html#filter(java.util.Collection,%20org.apache.commons.collections.Predicate)
If you, like me, don't like modifying a collection while iterating through it's elements or if the iterator just doesn't provide an implementation for remove, you can use a temporary collection to just collect the elements you want to delete. Yes, yes, its less efficient compared to modifying the iterator, but to me it's clearer to understand whats happening:
List<Object> data = getListFromSomewhere();
List<Object> filter = new ArrayList<Object>();
// create Filter
for (Object item: data) {
if (throwAway(item)) {
filter.add(item);
}
}
// use Filter
for (Object item:filter) {
data.remove(item);
}
filter.clear();
filter = null;

Categories

Resources