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.

Java Streams - Action and Continue

I was talking to someone who wanted to be able to perform an action on a stream without causing a terminal operation. To me the solution seemed obvious; use a map operation which returns the input for the output after performing an action on the object.

Consider an example where we have a List<String> that we wish to map/reduce and perform an action on the reduction, but we also want a count of the entities we encounter.

List<String> strings = Arrays.<String>asList(
        "k-1", "k-2", "k-3", "x-4", "x-5", "x-6", "k-7");
long p = strings.stream()
        .filter(n -> n.startsWith("k-")) // Filter starting with 'k-'
        .map(n -> n.substring(2))        // Map to number in string.
        .filter(NumberUtils::isDigits)   // Include integers only.
        .map(n -> Integer.parseInt(n))   // Map to int
        .map(n -> {
            System.out.println(n);       // Perform action.
            return n;                    // Return input as output.
        })
        .count();                        // Terminate stream.
        System.out.format(
                "There were %d items of %d processed.\n",
                p, strings.size());

Above in steps 1 - 4 we perform a series of map/reduce operations to retrieve the integer portion of the string "k-{n}". In steps 5-6 here we would normally use a forEach terminal operation we instead use a map intermediate operation which allows us to perform an action, returning the input so the stream stays open. In step 7 we finally invoke the terminal operation count which terminates the stream.

Note: in the above I have multiple filter/map operations that could be combined into single operations, that is perfectly fine; I recommend separating the tasks as it helps express your intention which can make it more readable.