views:

110

answers:

4

I have to iterate through an arraylist in this manner.

ArrayList<Integer> li = new ArrayList<Integer>();
li.add(20);
li.add(30);
li.add(40);
li.add(50);
li.add(70);

for (int i = 0; i < li.size() - 1; i++)
    System.out.println(li.get(i) + " " + li.get(i + 1));

Output:

20 30
30 40
40 50
50 70

How to do the same using an iterator?

+1  A: 

It's a bit more complicated:

Iterator<Integer> iter = li.iterator();
if (iter.hasNext()) {
    for (int y = iter.next(), x; iter.hasNext(); y = x) {
        x = iter.next();
        System.out.println(y + " " + x);
    }
}

or:

if (iter.hasNext()) {
    for (int x = iter.next(); iter.hasNext();) {
        System.out.println(x + " " + (x = iter.next()));
    }
}
Thomas Mueller
without a hasNext() check, your .next() call may throw a NoSuchElementException if the list has an odd number of items. http://download.oracle.com/javase/1.4.2/docs/api/java/util/Iterator.html#next%28%29
Ryan
This assumes always an even number of items in the iterator
Noel M
There *is* a hasNext() check before each next() call. Both options work correctly with any number of entries (0, 1, 2, 3, 4, 5, 6, 7,...). Well, if you don't believe me, try it out yourselves.
Thomas Mueller
The second one is a little bit risky, as it relies on left to right evaluation order for string concatenation to work properly... I know that the Java spec guarantees that, but it's still a not recommended practice (http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html)
fortran
+5  A: 

Use two iterators. I tested this and it worked for me.

    Iterator<Integer> first = li.listIterator();

    // Will raise IndexOutOfBoundsException if list is empty.
    Iterator<Integer> second = li.listIterator(1);

    while (second.hasNext()) {
        System.out.println(first.next() + " " + second.next());
    }

Edit: No need for inner if. Silly me.

Explanation: the listIterator(index) method returns an iterator that starts at the specified position in the list where as listIterator() returns an iterator that starts at position zero.

The first iterator therefore starts at 0 and the second starts at 1. Then it is merely a question of printing the next() on both. This will work irrespective of whether the number of elements in the list is odd or even.

Edit 2

My logic fu is very weak today. Thanks @barjak for pointing out the case about the empty list and the unnecessary first.hasNext().

Manoj Govindan
1) This code does'nt work if the list is empty. 2) `first.hasNext()` is useless, you can remove it. 3) An important thing to note is that the two calls to `next()` are necessary, even if the returned value is not used.
barjak
+1  A: 

Slightly Modified Sagar V's solution to make it work. One iterator is enough to achieve this.

Iterator iter = li.iterator();
        Integer int1 = null;

        while (iter.hasNext()) {
            if (int1 == null) {
                int1 = (Integer) iter.next();
            }

            System.out.print(int1 + " ");
            if (iter.hasNext()) {
                Integer int2 = (Integer) iter.next();
                System.out.println(int2);
                int1 = int2;
            } else {
                break;
            }
        }
    }
johnbk
perfect! Thanks :)
Sagar V
@Sagar - Welcome!
johnbk
A: 

There are many solutions, that can be appropriate depending on your needs.

Solution 1 : fixed-size view

The classical way to iterate on a subset of a list is to create a narrower view of the original list, and to iterate on this whole view. The subList method is used to create such a view.

List<Integer> l = // initialization code

int limitIndex = Math.max(l.size()-1, 0); // needed for empty list
for (int v : l.subList(0, limitIndex)) {
    // your code
}

Note that I used a 'foreach' loop, which is a convenient way to use iterators. This is strictly equivalent to this code :

Iterator<Integer> it = l.subList(0, limitIndex).iterator();
while(it.hasNext()) {
    int v = it.next();
    // your code
}

Also, note that the subList method does not create a new list : it only creates a 'view' on the original list. The content of the original list is not copied, and all changes made to the original list are visible from the list created by subList.

Solution 2 : a custom Iterator/Iterable

If all you need is an iterator that always iterates from 0 to n-1, you can define a new Iterable tailored to that particular need.

public class NoLastIterable<T> implements Iterable<T> {

    private final List<T> backend;

    public NoLastIterable(List<T> backend) {
        this.backend = backend;
    }

    public Iterator<T> iterator() {
        return new Iterator<T>() {

            private int nextIndex;

            public boolean hasNext() {
                return nextIndex < backend.size() -1;
            }

            public T next() {
                return backend.get(nextIndex++);
            }

            public void remove() {
                throw new UnsupportedOperationException("not implemented");
            }

        };
    }
}

This class is used like this :

for (int v : new NoLastIterable<Integer>(l)) {
    // your code
}

Solution 3

You can even create your custom view of a List, just like subList does, but with more flexibility.

public class NoLastList<T> extends AbstractList<T> {

    private final List<T> backend;

    public NoLastList(List<T> backend) {
        this.backend = backend;
    }

    @Override
    public T get(int index) {
        if (index >= size()) {
            throw new IndexOutOfBoundsException();
        }
        return backend.get(index);
    }

    @Override
    public int size() {
        return Math.max(0, backend.size()-1);
    }

}

Same usage as the other solutions :

for (int v : new NoLastList<Integer>(l)) {
    // your code
}

The benefits of this solution can be seen in the following situation :

  1. the original list is created and initialized
  2. the NoLastList instance is created (as a view of the original List)
  3. some elements are added to the original list

In this situation, iterating over NoLastList will take into account the elements that were added lately. NoLastList always represents a view of the elements from 0 to n-1, even if n (the size) changes.

barjak