JewelRuby

... bytes and babes of JRuby

Java Streams Meet Enumerator

Java 8 brought some interesting functional aspects to the Java standard library - a fairly decent support for transforming streams of data. Ruby, obviously, has its own perks to do quite more (as binding and closures are first class) but there’s no reason to be left out … on JRuby.

And so, without much fanfare, Enumerator implements java.util.Iterator and adds a stream helper. We’ve touched on Java streams previously, the same is feasible using a Ruby array (block-less each returns an enumerator) :

numbers = [ 1, 2, 3, 4, 5, 6, 7, 8 ]

supplier = -> { Array.new } # Supplier<R>
accumulator = -> (c, e) { c << e } # BiConsumer<R,? super T>
combiner = -> (c1, c2) { c1.addAll(c2) } # BiConsumer<R,R>

three_even_squares = numbers.each.to_java.stream.
  filter( ->(n) { puts "filtering #{n}"; n % 2 == 0 } ).
  map( ->(n) { puts "  mapping #{n}"; n * n } ).
  limit(3).collect(supplier, accumulator, combiner).
  to_s # an Array of "[ 4, 16, 36 ]"

… or an example with an explicit Enumerator calculating fibonacci till the end :

enum = Enumerator.new do |out|
  out << prev = 1; sum = 1
  loop { prev, sum = sum, prev + sum; out << sum }
end

enum.to_java.stream.
     filter( ->(n) { n > 100 } ).
     limit(10).forEach { |n| puts n }

# prints 10 numbers from the sequence skipping <= 100
JRuby >= 9.1.3 is needed for a Java stream-able Enumerator experience.

On a final note, using parallel streaming does not make much sense. Although (non-blocking) thread-safe Enumerator instances are achievable, parallelizing iteration over such won’t provide the desired benefit until JRuby’s underlying thread-based implementation details are synchronizing over next-ed values.