13

Personally, I find the range of functionality provided by java.util.Iterator to be fairly pathetic. At a minimum, I'd like to have methods such as:

  • peek() returns next element without moving the iterator forward
  • previous() returns the previous element

Though there are lots of other possibilities such as first() and last().

Does anyone know if such a 3rd party iterator exists? It would probably need to be implemented as a decorator of java.util.Iterator so that it can work with the existing java collections. Ideally, it should be "generics aware".

Thanks in advance, Don

Dónal
  • 185,044
  • 174
  • 569
  • 824

11 Answers11

11

You can get previous() easily by just using a java.util.ListIterator.

Peek at that point is easily implemented by doing a

public <T> T peek(ListIterator<T> iter) throws NoSuchElementException {
    T obj = iter.next();
    iter.previous();
    return obj;
}

Unfortunately it will be easier to have it as a utility method since each collection class implements their own iterators. To do a wrapper to get a peek method on each collection on some interface such as MyListIterator would be quite a lot of work.

Jakobinsky
  • 1,294
  • 8
  • 11
William
  • 6,338
  • 4
  • 32
  • 36
8

I think the reason these aren't implemented is because they are non-trivial for some collections and would have large performance impact. I think it would be pretty simple for you to make this work for the collections you care about.

I also don't like that Java iterators have no way of getting the current value without moving it (and therefore you can't easily write code that branches based on the value, just passing the iterator -- you have to pass the value you now have as well).

Lou Franco
  • 87,846
  • 14
  • 132
  • 192
8

Apache Commons Collections

Google Collections

ykaganovich
  • 14,736
  • 8
  • 59
  • 96
4

There is a good damn reason the generic operators do not implement these features: they do not exist for all container. The typical example is a container representing some external data input, like a file seen as a stream. Each time you read a value you consume it and move the pointer forward, if you want it or not. If you impose these constraints on the generic iterators, then you loose the genericity of the iterators.

If you want a previous method, as suggested, use the ListIterator<>, which is then restricted to container behaving as lists.

PierreBdR
  • 42,120
  • 10
  • 46
  • 62
  • The file argument is not really convincing, there is no reason why we could not get the current character (or whatever it is we are reading) without advancing the cursor in the file. However, I agree that the `previous` feature cannot be provided by every iterator (especially "generator" iterators, which generates a new value at each step). – Luc Touraille Jun 30 '11 at 09:10
  • @Luc: this is because you see files as random access. And they really don't have to be. If you prefer, consider a stream such as network sockets. You read what you receive, and if you want to read ahead, you need a whole caching mechanism. So if you want your iterator to be used in all situation, you *really* need the interface Java is providing. After that, you may use adaptors, or more specialised iterators. It stays that the interface Java provide is the most sensible one for generic iterators. – PierreBdR Jun 30 '11 at 20:30
  • This is not a matter of random access: even on a forward only stream, I fail to see a reason why accessing the value that has just been read and reading the next one should be considered as a single operation. But I guess this is rather subjective, and that we'll have to agree to disagree :). However, I'd like to add that I find it a bit odd to have a `remove` method on an interface which is supposed to be very generic, when removing is an operation that is only possible on a rather limited set of iterable objects. – Luc Touraille Jun 30 '11 at 23:02
3

One thing I would look at is the Seq implementation in clojure

http://clojure.org/sequences

The implementation of the base classes are in Java and full source is available. Seqs are decorators on java iterators (take and implement java iterator interfaces) -- but they also provide their own interface which might be more of what you want -- or at least a starting point.

Lou Franco
  • 87,846
  • 14
  • 132
  • 192
2

I saw that someone linked to Google Collections, but no one mentioned that the method you are looking for is called Iterators.peekingIterator().

Still, it would be best if you could just use a ListIterator.

Kevin Bourrillion
  • 40,336
  • 12
  • 74
  • 87
1

As ykaganovich suggested, you might want to check out the google-collections stuff. There is definitely some support for some of the things you want, like peeking. Also, as some others have mentioned, implementing all of these things for all collections can be dangerous from a possibility or performance viewpoint.

Paul Wicks
  • 62,960
  • 55
  • 119
  • 146
1
public class Iterazor<T> {
  private Iterator<T> it;
  public T top;
  public Iterazor(Collection<T> co) {
    this.it = co.iterator(); 
    top = it.hasNext()? it.next(): null; 
  }
  public void advance() { 
    top = it.hasNext()? it.next(): null; 
  }
}

// usage

for(Iterazor<MyObject> iz = new Iterazor<MyObject>(MyCollection); 
    iz.top!=null; iz.advance())
  iz.top.doStuff();
}
0

I haven't ever run into an issue where I've needed a peek(); Iterator has worked just fine for me. I'm curious how you're using iterators that you feel you need this added functionality.

Steve g
  • 2,471
  • 17
  • 16
  • I think the most common case is some kind of dispatch. You want to read the first element to see who should handle it and dispatch -- if the receiver needs the whole sequence it's nice to just pass that and not have to also pass the removed object. – Lou Franco Oct 10 '08 at 18:01
  • I can see that. It just has the smell to it of something that could be done much easier some other way like a visitor or something. – Steve g Oct 10 '08 at 18:05
0

It sounds like you might be better off using a Stack.

Draemon
  • 33,955
  • 16
  • 77
  • 104
0

The Java collections were written to provide a minimal set of useful functionality. This is a very good approach for code that has to be implemented by anyone implementing Java. Bloating an interface with functionality that might be useful can lead to a sizable increase in volume of code with improvements noticed only by a few. If peek() and previous() were part of the standard iterator that means everyone writing a new kind of Collection must implement it, whether it's sensible or not.

Iterators are also designed to work on things that physically cannot go backwards, making peek() and previous() both impossible.

DJClayworth
  • 26,349
  • 9
  • 53
  • 79