Matt Lindsay
Matt Lindsay

Reputation: 55

Why does the Iterator not "Move Next" in a for loop

I'm learning to iterate, and have implemented an Iterator on my 'CStickChart' Class using the following private property:

  private List<CStick> cStickCollection = new ArrayList<CStick>();

and then implementing the method to return CSticks:

  public Iterator<CStick> iterator() {
    return this.cStickCollection.iterator();
  }

Now when I try and iterate through it, I'm able to do so with the assigned localCStick but calling the next() method on the CStickChart Iterator doesn't do what I expected it to. I expected it to give me the next CStick in my CStickChart (hence when I call the getEPIC I was expecting it to give me the next EPIC along).

// Print the EPIC out of the Array using the iterator
for (CStick localCStick : testCStickChart) {
  System.out.println(localCStick.getEPIC());
  //The below line doesn't return the next CStick and I'm not sure why
  System.out.println("next EPIC is " + testCStickChart.iterator().next().getEPIC());
}

Please could someone explain why this is not the case (it always returns the first EPIC)

Upvotes: 1

Views: 6497

Answers (4)

Makoto
Makoto

Reputation: 106490

It sounds like you don't want to use the enhanced-for structure. The reason: an enhanced-for with an iterable entity will use the iterator provided internally, and will only ever advance forward.

This also means that any calls to a iterator while inside that loop produce an iterator that starts at the beginning of iteration.

So, with that, you have two options - both of which involve abandoning the enhanced-for:

  • Use a standard for loop with indexing to advance backwards and forwards with the list, or
  • Use a ListIterator as provided by List to move backwards and forwards in a very seamless way.

Here is an example with using integers - note that every time I advance the iterator I have to move it back to its previous spot so that I don't double-advance it. Also, I have a condition to break out of the loop once we've run out of elements.

List<Integer> integerList = new ArrayList<Integer>() {{
    add(1);
    add(2);
    add(3);
    add(4);
    add(5);
    add(6);
    add(7);
    add(8);
    add(9);
    add(10);
}};

for (ListIterator<Integer> iterator = integerList.listIterator(); iterator.hasNext(); ) {
    int value = iterator.next();
    int nextValue = Integer.MIN_VALUE;
    if (iterator.hasNext()) {
        nextValue = iterator.next();
        // Reset the state of the iterator
        iterator.previous();
    }

    System.out.println("Value = " + value);
    if(nextValue != Integer.MIN_VALUE) {
        System.out.println("Next value = " + nextValue);
    }
}

Upvotes: 1

diogo
diogo

Reputation: 79

Everytime you call testCStickChart.iterator() inside of that loop, you create a new iterator object. So each call to next() is carried out on a new iterator object, returning the first object. What you want to do is to declare a new Iterator<CStick> just before the loop and use it inside the loop, like so:

Iterator<CStick> it = testCStickChart.iterator();
// Print the EPIC out of the Array using the iterator
for (CStick localCStick : testCStickChart) {
    System.out.println(localCStick.getEPIC());
    //The below line doesn't return the next CStick and I'm not sure why
    System.out.println("next EPIC is " + it.next().getEPIC());
}

Upvotes: 1

Elliott Frisch
Elliott Frisch

Reputation: 201507

Because you are getting the top iterator of cStickCollection with .iterator(). I think you wanted to use the same iterator position as you're at in your loop, and peek at the next element. You can't do that with a for-each loop and you also can't do that with an Iterator in general (because they don't implement a peek).

Instead, you could use a traditional for loop on your cStickCollection like

for (int i = 0, len = cStickCollection.size(); i < len; i++) {
    CStick localCStick = cStickCollection.get(i);
    System.out.println(localCStick.getEPIC());
    if (i + 1 < len) { // <-- check that there is a "next"
        System.out.println("next EPIC is "+cStickCollection.get(i+1).getEPIC());
    }
}

Upvotes: 0

Jesper
Jesper

Reputation: 206926

System.out.println("next EPIC is " + testCStickChart.iterator().next().getEPIC());

This happens because in this line you are getting a new iterator in every iteration of the loop. Each new iterator starts from the beginning of the list again.

Upvotes: 1

Related Questions