Iterator off by 1 confusion in Java - java

When using iterator in Java, the last iterator is pointing one past the last node right? And the value is null right?
Then when using
iter = myList.listIterator();
sum = 0;
while (iter.hasNext()) {
sum += iter.next();
}
Shouldn't there be a off-by-one problem since the last value iterator is pointing at is null? I know in fact is there's no such off-by-one problem, but don't know why, please explain this for me. Thanks in advance!
Edit: sorry, too excited, didn't type complete code.
Summary: Hope this helps for future people, I was thinking about iterators as pointers in C++, but actually even if imagined as pointers, instead of pointing to nodes, they 'point' to the space between nodes (space before a node to be more precisce) as bmorris591 described.

next() moves the Iterator along, hasNext() only checks if there is a next. The Iterator doesn't "point", if anything it points at the spaces between elements.
You may want to read up on the Iterator Pattern.

There is no null element returned from an Iterator when hasNext returns false.
Conceptually, when hasNext returns false, the iterator is pointing "past" the last element already, but there is nothing "past" the last element, not even null.
If you call next when hasNext returns false, then it won't return null -- it will throw a NoSuchElementException.

An iterator iterates over the list. The method hasNext() returns true when there are still elements left to iterate over and false when all elements have been processed. An example:
A list with contents [0,2,4]:
iter = myList.listIterator();
iter.hasNext(); // true
iter.next(); // returns 0
iter.hasNext(); // true
iter.next(); // returns 2
iter.hasNext(); // true
iter.next(); // returns 4
iter.hasNext(); // false
Thus there is no problem with off-by-one or something like that. Whenever hasNext() returns true, there are still elements left to iterate over; next() returns that element.

Assuming your list contains Integer instances, you can do something like the following:
Iterator <Integer> iter = myList.listIterator();
int sum = 0;
while (iter.hasNext()) {
sum += iter.next().intValue();
}
your code will add the result of each iter.next() to sum on each iteration of the loop.
When iter.hasNext() is called, it is not "at the first value", but just before the first value - the first call will then return that first value and update the internal state of the iterator to be just before the second value (just after the first value).
When you get to the end, the internal state is pointing to just after the last value, so iter.hasNext() returns false and your loop exits.
Now, if your iterator contains any null values, they are still returned by the calls to next(), and you'll end up with a NullPointerException (which you can avoid with a simple null check).

Related

hasnext() in iterator is not working as expected

I have difficulty in using hasNext() iterator method. I have a JSONArray:
JSONArray = [{"a":1},{"b":2,"c":3}]
I am accessing one JSONObject at a time. First JSONObject in the JSONarray has one element second have two elements. The issue is that when the iterator check hasNext() on first JSONObject with one element it gives true, my understanding is that it should give true only if it has more elements then the present one. Please help and clarify this.
for (int i=0; i<JArrayLength; i++) {
JSONObject obj = newJArray.getJSONObject(i);
Iterator k = obj.keys();
System.out.println("Value k.hasnext is = " + k.hasNext());
if(k.hasNext())
{ //print somehting} // here its printing but as the value should be false it should not for i=0.
}
Please suggest where am I going wrong.
You open the iterator
Iterator k = obj.keys();
and then immediately check to see whether it has keys. Since you haven't consumed the first key (a), of course it does.
Note also that you're getting warnings about using the raw type Iterator. Pay attention to them and parameterize appropriately (probably with <String>).
hasNext checks the iterator if it has any values to iterate to actually get the value you should do:
if(k.hasNext()){
k.next(); // gets the actual value
}
hasNext() Returns true if the iteration has more elements.
next() Returns the next element in the iteration.

Iterator.hasNext() does not appear to return false on the last item in a List

I have an if-else function within a for loop iterating through a list. When I reach the last element of this list, I want it to undertake an action, but it doesn't seem to be working. Here is an outline of my code:
for(Item t: itemList){
if(....){
}
else if(....){
}
else if(currentStartTime>previousFinishTime){
System.out.println("C");
if(itemList.iterator().hasNext()==false){
System.out.println("end of array");
EFTs.add(EFT);}
else{.....;}
}
When I trigger this condition with the last item in the list (i.e. the last item has currentStartTime>previousFinishTime, I know this is correct because it prints C), nothing in my if condition triggers. Have I misunderstood the purpose of the hasNext() function?
Thanks
itemList.iterator().hasNext()==false
itemList.iterator() refers to a brand new iterator. It doesn't refer to the iterator being used in the for loop. itemList.iterator() will in fact always start at the beggining of the list, and thus hasNext() will tell you if the list is empty.
To use an iterator like this, you need to make your own loop, something like:
for(Iterator<Item> iter = itemList.iterator(); iter.hasNext(); ) {
Item item = iter.next();
if(iter.hasNext()) {
}
}
But 95% of the time, you should be able to do whatever you want after the loop instead of during the last iteration.
With the "enhanced for", you cannot access the operator created for the for operation.
What you are doing is creating a new operator each time, so it points to the start of the list. Obviously, unless it is an empty list, it will return true for hasNext().

Java list's .remove method works only for second last object inside for each loop [duplicate]

This question already has answers here:
Why is a ConcurrentModificationException thrown and how to debug it
(8 answers)
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 3 years ago.
I am seeing a weird behavior.
List<String> li = new ArrayList<>();
li.add("a");
li.add("b");
li.add("c");
li.add("d");
li.add("e");
for(String str:li){
if(str.equalsIgnoreCase("d")){
li.remove(str); //removing second last in list works fine
}
}
But if i try to remove any other than second last in the list, i get ConcurrentModificationException. It came to my attention while reading "Oracle Certified Associate Java SE 7 Programmer Study Guide 2012" which incorrectly assumes that .remove() always works with an example of removing the second last in the list.
In a list, adding or removing is considered as a modification. In your case you have made
5 modifications(additions).
‘for each’ loop works as follows,
1.It gets the iterator.
2.Checks for hasNext().
public boolean hasNext()
{
return cursor != size(); // cursor is zero initially.
}
3.If true, gets the next element using next().
public E next()
{
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
final void checkForComodification()
{
// Initially modCount = expectedModCount (our case 5)
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
Repeats steps 2 and 3 till hasNext() returns false.
In case if we remove an element from the list , it’s size gets reduced and modCount is increased.
If we remove an element while iterating, modCount != expectedModCount get satisfied
and ConcurrentModificationException is thrown.
But removal of second last object is weird. Lets see how it works in your case.
Initially,
cursor = 0 size = 5 --> hasNext() succeeds and next() also succeeds
without exception.
cursor = 1 size = 5 --> hasNext() succeeds and next() also succeeds
without exception.
cursor = 2 size = 5 --> hasNext() succeeds and next() also succeeds
without exception.
cursor = 3 size = 5 --> hasNext() succeeds and next() also succeeds
without exception.
In your case as you remove ‘d’ , size gets reduced to 4.
cursor = 4 size = 4 --> hasNext() does not succeed and next() is
skipped.
In other cases, ConcurrentModificationException will be thrown as modCount != expectedModCount.
In this case, this check does not take place.
If you try to print your element while iterating, only four entries will be printed. Last element is skipped.
Hope I made clear.
Don's use List#remove(Object) here since you are accessing elements from the List in for-each loop.
Instead use Iterator#remove() to remove an item from List:
for(Iterator<String> it=li.iterator(); it.hasNext();) {
String str = it.next();
if(str.equalsIgnoreCase("d")) {
it.remove(); //removing second last in list works fine
}
}
ConcurrentException is raised because of the fail fast behaviour of the ArrayList. This means you can not modify the list while iterating it except Iterator#remove() .
Refer http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html
Please use Iterator#remove() method while removing elements from a List while looping . Internally the for-each loop will use the Iterator to loop through the Listand since the behavior of an Iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling Iterator's remove() method.You are getting the exception .
This loop :
for(String str:li){
if(str.equalsIgnoreCase("d")){
li.remove(str); //removing second last in list works fine
}
}
is basically
Iterator<String> itr = li.iterator();
while(itr.hasNext()){
String str = (String)itr.next();
if(str.equalsIgnoreCase("d")){
li.remove(str); //removing second last in list works fine
}
}
Why removing second last element doesn't throw exception ?
Because by removing the second last element you have reduced the size to the number of elements which you have iterated over. A basic hasNext() implementation is
public boolean hasNext() {
return cursor != size;
}
So in this case the cursor=size=4, so hasNext() evaluates to false and the loop breaks early before the concurrent modification check is performed in next(). The last element is never accessed in this case . You can check that by adding a simple OR condition in the if
if(str.equalsIgnoreCase("d") || str.equalsIgnoreCase("e")){
// last element "e" won't be removed as it is not accessed
li.remove(str);
}
But if you remove any other element next() is called which throws the ConcurrentModificationException.
If you really want to iterate over an ArrayList and remove elements then you should do it this way:
for(int index = yourArrayList.size() - 1; index >= 0; index--) {
if(yourCondition) {
yourArrayList.remove(index);
}
}
You can iterate "backwards" and remove elements, but not forward. So instead of iterating from the first element to the last, iterate from the last to the first.
Pseudo-code:
for(int i = list.size() -1; i >= 0; i--)
{
list.remove(i);
}
If you wish to remove all then.removeAll should do the trick rather than iterating through the collection. I think it is speedier too.

iterator returning random letters can't get it to iterate

I have this iterator,
Set<BigFraction> key = knowledgeD.keySet();
TreeSet<BigFraction> sortKey = new TreeSet<BigFraction>();
sortKey.addAll(key);
Iterator<BigFraction> iter = sortKey.iterator();
return iter;
BigFraction is just the data type if it makes it any easier just sub this with int or something.
Anyway when i called the iterator later on to
while (iterator().hasNext());
It basically just gives me an infinite loop of somesort... and when i printout iterator() before this while loop i get
java.util.TreeMap$KeyIterator#53b4b24d
Any idea's kind of stuck,
Regards,
Sim
You need to use your Iterator like this:-
while (iter.hasNext()) {
System.out.println(iter.next()); //Do whatever you want
}
where hasNext() tells if the iterator has more values or not, and next() returns the next value in the iterator.
Also, the explanation of why your current iterator loop is not working(going into an infinite loop), has been explained by #jacobm.
iterator().hasNext() never advances the iterator, it just checks to see if it has an element. So while (iterator().hasNext()); will always infinite-loop if there are any values to iterate over. You can fix it with:
Iterator<BigFraction> it = iterator();
while (it.hasNext()) {
it.next();
}
The call to iterator() constructs a new iterator, so your code will construct a new iterator each time through the loop. Instead try something like:
Iterator it = iter;
while (it.hasNext()) {
it.next()
}
Be sure to call next() each time through the loop, otherwise you won't increment the iterator, and it will just stay on the first item.
Iterator#hasNext returns true if the iteration has more elements, it does not return the next element in the iteration. which Iterator#next does.
If you use itarator if you must call iterator#next to get the next element of collection, but hasNext is operation is optional.
you could either write -
while (iter.hasNext()) {
Object oj = iter.next();
}
or
Object obj = null;
while ( (obj = iter.next())!=null) {
....
}
with this approach hashNext is not at all required.

How to determine last element when using an iterator?

I love to use a for loop with the iterator principle, like
for(String s : collectionWithStrings)
System.out.println(s + ", ");
Question: How can I determine if the current element is the last one?
With an own index like int = 0; i < collection.size(); i++ this is possible with i == collection.size() - 1, but not nice. Is it also possible to determine the last element with an iterator for the example above?
Indeed, the Iterator#hasNext method returns a boolean determining if the iterator will return another element with the next method.
Your iteration can be put as this:
Iterator<String> iterator = collectionWithString.iterator();
while(iterator.hasNext()) {
String current = iterator.next();
// if you invoke iterator.hasNext() again you can know if there is a next element
}
Just use the hasNext method.
if(!iterator.hasNext()) {
// this is the last element
}
Normally, we iterate using an Iterator as so:
while(iterator.hasNext()) {
Object obj = iterator.next();
}
It's not possible with an enhanced for loop without maintaining your own counter. To be honest, this is my one deciding factor when I choose which type of for loop to use.
When using an Iterator, one has access to the hasNext() method which will return false when you are processing the last element.

Categories