We're updating the issue view to help you get more done. 

Most Iterator implementations do not correctly implement next failing to throw the required NoSuchElementException

Description

Iterators on Clojure's collections should follow the expected JDK behavior of throwing NoSuchElementException on next() when an iterator is exhausted. Current collections have a variety of other behaviors.

Issue encountered in real world code using http://pipes.tinkerpop.com.

To reproduce:

1 (-> [] .iterator .next)

This throws a NPE instead of NSEE.

1 (doto (.iterator [1 2]) .next .next .next)

This throws an ArrayIndexOutOfBoundsException instead of NSEE.

An additional problem found during testing is that subvecs will iterate past the subvector end and produce data from the underlying source vector:

1 2 3 4 5 6 user=> (def iter (.iterator (subvec [4 5 6 7] 0 1))) user=> (dotimes [_ 3] (println (.next iter))) 4 5 ;; should have thrown an exception here 6 nil

Approach: The attached patch fixes the methods by adding a check for hasNext before actually trying to provide the next element. If there is no next element the correct exception is thrown.

Performance:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (use 'criterium.core) (def v (vec (range 10000))) (def sm (into (sorted-map) (zipmap (range 10000) (range 10000)))) (def pv (apply vector-of :long (range 10000))) (defn consume-iter [^Iterable src] (let [^java.util.Iterator i (.iterator ^Iterable src)] (loop [c []] (if (.hasNext i) (recur (conj c (.next i))) c)))) (bench (consume-iter v)) (bench (consume-iter sm)) (bench (consume-iter pv))

coll

1.8.0-alpha5

-2 patch

-3 patch

v

313 us

314 us

306 us

sm

723 us

741 us

708 us

pv

516 us

546 us

520 us

Patch: clj-1453-3.patch and tests: CLJ-1453-tests.patch

Screened by:

Environment

None

Status

Assignee

Alex Miller

Reporter

Meikel Brandmeyer

Labels

Approval

Ok

Patch

Code and Test

Fix versions

Affects versions

Release 1.6

Priority

Major