Tuesday, May 20, 2014

Stream for java.util.Enumeration

While it is encouraged to use a Iterator in preference to a Enumeration, the Enumeration is still widely used. One such use is within ServletRequest#getAttributeNames which returns an Enumeration<String>.

With JSR-335 it's much more expressive to use a lambda while traversing any form of collection, but the language does not (yet) contain support for turning an Enumeration into a stream. There is support to do this for a Collection.

We can add our own support for this by implementing our own static stream helper class. First we need to implement a Spliterator for enumerating Enumeration instances. We then create helper methods for returning a Spliterator and Stream instance.

public class StreamHelper {

    public static <T> Spliterator<T> spliterator(Enumeration<T> n) {
        return new EnumerationSpliterator<>(n);
    }

    public static <T> Stream<T> stream(Enumeration<T> en) {
        return StreamSupport.stream(spliterator(en), false);
    }


    public static class EnumerationSpliterator<T>
            implements Spliterator<T> {

        private final Enumeration<T> en;

        public EnumerationSpliterator(Enumeration<T> e) {
            this.en = e;
        }

        @Override
        public boolean tryAdvance(Consumer<? super T> action) {
            if (en.hasMoreElements()) {
                action.accept(en.nextElement());
                return true;
            }
            return false;
        }

        @Override
        public Spliterator<T> trySplit() {
            return null;
        }

        @Override
        public long estimateSize() {
            return Long.MAX_VALUE;
        }

        @Override
        public int characteristics() {
            return IMMUTABLE;
        }

    }

}

We can then turn any Enumeration into a stream simply.

StreamHelper.stream(req.getParameterNames())
        .forEach(System.out::println);


Notes on Implementation


There are a couple of notes for streams on enumeration instances. Firstly the enumeration is not as flexible as an iterator, they can not be updated, do not allow us to find out how many elements exist, and can not be partitioned.

For these reasons we must return Long.MAX_VALUE from estimateSize, return IMMUTABLE from characteristics and return null from trySplit. Since we know that it's not possible to split the stream we know we can't process the stream in parallel, so we call StreamSupport.stream(spliterator(en), false); to create the stream.

Summary


Creating streams from any source isn't too difficult, the ingredients you most often require is a Spliterator. This example should help you to create your own spliterator's to allow you to traverse more complex structures.