-1

Is it possible to assert the only value of the 4th object within an iterator?

I have seen examples of people looping over a list with an Iterator, but I don't want to get out the objects held in index 0 to 2, but just that at index 3...

I know that I could always do .next().next().next(), but that looks a little... silly. Is there any way in which I can do something like .next(4) to go direct?

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
physicsboy
  • 5,656
  • 17
  • 70
  • 119

4 Answers4

2

Iterators don't work with index and they don't know at what index the last item appears. Their only purpose is to iterate through elements until the end is reached.

It means, if you want to work with the 4th element, you have to call Iterator::next() four times. A situation there are only 3 or fewer elements in the iterator may happen, so you have to check against Iterator::hasNext(), otherwise when there is no more element and you call next() again, the NoSuchElementException is thrown.


Alternatively, since Java-8 you can use the method Iterator::forEachRemaining(Consumer<? super E> action) which performs the given action for each of the remaining elements. Also, you need to use a trick with the array to avoid the effectively final variable constraint within the lambda expression. Sadly, this method doesn't accept BiConsumer<T,U> as an argument to consume not only the element but also its index - that's why this trick.

List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
int index[] = new int[]{0};

// Prints 4 and 8
Iterator<Integer> iterator = list.iterator();
iterator.forEachRemaining(i -> {
    if ((index[0]+1)%4 == 0) {
        // Here you work both with the element `i` and its index `index[0]`
        System.out.println(i);     
    }
    index[0]++;
});
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
1

Use Guava Iterators

Guava has a common utility classes for this proposes. The exact answer in this terms will be:

In this example there is some risk to encounter IndexOutOfBoundsException if position supplied is advance iterator beyond its bounds.

@Test
public void getNthElement() {
    List<String> alphabet = Arrays.asList("a", "b", "c", "d");
    Iterator<String> iterator = alphabet.iterator();

    int position = 3;
    String forthElement = Iterators.get(iterator, 3);

    assertEquals("d", forthElement);
}

The Iterators has a convenient overload for get(Iterator<T>, int) method so you'll never get a IndexOutOfBoundsException.

@Test
public void getNthElementWithDefaultValue() {
    List<String> alphabet = Arrays.asList("a", "b", "c", "d");
    Iterator<String> iterator = alphabet.iterator();
    int position = 10;
    final String actual = Iterators.get(iterator, position, "default");

    assertEquals("default", actual);
}

If its necessary to just skip or reach to specific position, but here comes the @Nikolas's mentioned case. Here you can easily get the NoSuchElementException

@Test
public void advance() {
    List<String> alphabet = Arrays.asList("a", "b", "c", "d", "e", "f");
    Iterator<String> iterator = alphabet.iterator();

    // will return the number of skipped elements
    int advance = Iterators.advance(iterator, 3);

    assertEquals(3, advance);
    assertEquals("d", iterator.next());
}

@Test(expected = NoSuchElementException.class)
public void advanceBeyond() {
    List<String> alphabet = Arrays.asList("a", "b", "c");
    Iterator<String> iterator = alphabet.iterator();

    int advance = Iterators.advance(iterator, 3);

    // throws the exception because we advanced iterator to end
    iterator.next();
}

If you can't or don't want to depend on Guava

Here is the very simple (educational) implementation to reach iterator's position.

public static <T> T get(Iterator<T> iterator, int pos) {
    if (pos < 0) {
        throw new IllegalArgumentException("Position is negative!");
    }

    int i = 0;

    while (i++ < pos) {
        iterator.next();
    }

    return iterator.next();
}
Edgar Asatryan
  • 687
  • 9
  • 13
0

If you aren't restricted to the iterator, you can use Java 8 Streams to do something similar:

Object opt = collection.stream().skip(3).findFirst().orElse(null);
//opt will be null if there were 3 or fewer items in the collection
if(opt!=null) {}

//or to operate on the rest of the Stream
collection.stream().skip(3).forEach(...)
Thomas Timbul
  • 1,634
  • 6
  • 14
0

You can use AssertJ to concisely assert this:

Iterator<Integer> it = List.of(2, 4, 6, 8, 10).iterator();

assertThat(it).element(3).isEqualTo(8);
Jonathan
  • 20,053
  • 6
  • 63
  • 70