Forum: JRuby Working with java 8 streams in JRuby

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
3b8ad006781b1a3ab36dc76e2a05ce9c?d=identicon&s=25 Cris Shupp (cshupp)
on 2015-11-16 23:00
The following java code:

  public static void main (String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
    List<Integer> twoEvenSquares =
        numbers.stream()
               .filter(n -> {
                        System.out.println("filtering " + n);
                        return n % 2 == 0;
                      })
               .map(n -> {
                        System.out.println("mapping " + n);
                        return n * n;
                      })
               .limit(2)
               .collect(() -> new ArrayList<>(),
                           (c, e) -> c.add(e),
                           (c1, c2) -> c1.addAll(c2));
    System.out.println("Output: " + twoEvenSquares);
  }

yields:

filtering 1
filtering 2
mapping 2
filtering 3
filtering 4
mapping 4
Output: [4, 16]


The ostensibly equivalent JRuby code:

numbers = java.util.Arrays.asList(1,2,3,4,5,6,7,8)

filter_lamb = ->(n) {
  puts "filtering #{n}"
  n % 2 == 0
}
map_lamb = ->(n) {
  puts "mapping #{n}"
  n*n
}
l1 = -> { java.util.ArrayList.new }
l2 = -> (c,e) { c.add(e) }
l3 = -> (c1,c2) { c1.addAll(c2)}

two_even_squares =
numbers.stream().filter(filter_lamb).map(map_lamb).limit(2).collect(l1,l2,l3)



bombs out with:

ArgumentError: wrong number of arguments (1 for 2)

in the collect call.

What am I doing wrong?

Thanks,

Cris
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2015-11-20 13:19
I can not really recreate the issue, because I am using Java 1.7 (you
are doing it with Java 1.8, aren't you?), and it seams that 1.7 doesn't
have the method 'stream' yet, because I get the error message

   NoMethodError: undefined method `stream' for
#<Java::JavaUtil::Arrays::ArrayList:0x3534ef8c>

However, I see a few other suspicious points in your code:

- You are applying the method 'limit' to the result of 'map'. I am not
aware of the existence of this method, and could not find it in the core
docs. Could it be that you mean 'take' instead?

- The error message says

  "wrong number of arguments (1 for 2)"

which means "you supplied to 1 argument, but two where expected". Your
call to collect looks like

  collect(l1,l2,l3)

which passes 3 arguments. The Java version of 'collect' expects 3
arguments, but from the error message, we conclude not only, that a
different 'collect' is executed from what you expect, but it is also
weird that a different number of arguments is reported. So I ask my
self: Can you verify, that this is really THIS collect call, which is in
error, and not a different one in some other part of your program? Can
you reproduce this error with the code given, in jirb?

If yes, maybe you could print out the class name of the result of your
limits() call, to see whether this is really the class you are
expecting.
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2015-11-20 14:29
Correction to my previous post - there are two remarks in my post, which
are silly, so please ignore them.

(1) Of course you are working with Java 1.8. The title of your post says
it.

(2) You are not using the Ruby method Array.limit, so my remark about
limit doesn't make sense.

But I think, my remark about the number of arguments still applies.
3b8ad006781b1a3ab36dc76e2a05ce9c?d=identicon&s=25 Cris Shupp (cshupp)
on 2015-11-20 15:16
Ronald,

These new stream apis are only found in java 8.

Consider the following irb:

irb(main):001:0> JRUBY_VERSION
=> "9.0.4.0"
irb(main):002:0> numbers = java.util.Arrays.asList(1,2,3,4,5,6,7,8)
=> #<Java::JavaUtil::Arrays::ArrayList:0x2d3379b4>
irb(main):003:0>
irb(main):004:0* filter_lamb = ->(n) {
irb(main):005:1*   puts "filtering #{n}"
irb(main):006:1>   n % 2 == 0
irb(main):007:1> }
=> #<Proc:0x5e0e82ae@(irb):4 (lambda)>
irb(main):008:0> map_lamb = ->(n) {
irb(main):009:1*   puts "mapping #{n}"
irb(main):010:1>   n*n
irb(main):011:1> }
=> #<Proc:0x51399530@(irb):8 (lambda)>
irb(main):012:0> l1 = -> { java.util.ArrayList.new }
=> #<Proc:0x6b2ea799@(irb):12 (lambda)>
irb(main):013:0> l2 = -> (c,e) { c.add(e) }
=> #<Proc:0x411f53a0@(irb):13 (lambda)>
irb(main):014:0> l3 = -> (c1,c2) { c1.addAll(c2)}
=> #<Proc:0x2b71e916@(irb):14 (lambda)>
irb(main):015:0> two_even_squares
=numbers.stream().filter(filter_lamb).map(map_lamb).limit(2)
=> #<Java::JavaUtilStream::SliceOps::1:0x702657cc>
irb(main):016:0>
irb(main):017:0>
two_even_squares.java_class.java_instance_methods.map(&:to_s).reject do
|e| e !~ /.*collect.*/ end
=> ["public final java.lang.Object
java.util.stream.ReferencePipeline.collect(java.util.stream.Collector)",
"public final java.lang.Object
java.util.stream.ReferencePipeline.collect(java.util.function.Supplier,java.util.function.BiConsumer,java.util.function.Bi
Consumer)"]
irb(main):018:0> two_even_squares.java_class
=> class java.util.stream.SliceOps$1
irb(main):024:0>  two_even_squares.java_send :collect,
[java.util.function.Supplier,java.util.function.BiConsumer,java.util.function.BiConsumer],
l1, l2, l3
ArgumentError: wrong number of arguments (1 for 2)
        from org/jruby/java/proxies/JavaProxy.java:352:in `java_send'
        from (irb):24:in `<eval>'
        from org/jruby/RubyKernel.java:978:in `eval'
        from org/jruby/RubyKernel.java:1291:in `loop'
        from org/jruby/RubyKernel.java:1098:in `catch'
        from org/jruby/RubyKernel.java:1098:in `catch'
        from C:/languages/jruby/jruby-9.0.4.0/bin/jirb:13:in `<top>'



Cris
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2015-11-20 16:17
Cris Shupp wrote in post #1179416:
> irb(main):018:0> two_even_squares.java_class
> => class java.util.stream.SliceOps$1
> irb(main):024:0>  two_even_squares.java_send :collect,
>
[java.util.function.Supplier,java.util.function.BiConsumer,java.util.function.BiConsumer],
> l1, l2, l3
> ArgumentError: wrong number of arguments (1 for 2)
>         from org/jruby/java/proxies/JavaProxy.java:352:in `java_send'
>         from (irb):24:in `<eval>'
>         from org/jruby/RubyKernel.java:978:in `eval'
>         from org/jruby/RubyKernel.java:1291:in `loop'
>         from org/jruby/RubyKernel.java:1098:in `catch'
>         from org/jruby/RubyKernel.java:1098:in `catch'
>         from C:/languages/jruby/jruby-9.0.4.0/bin/jirb:13:in `<top>'

OK, so the problem is not the invocation of 'collect', but something
which happens inside 'collect', which means that, if the error is in
your code, it must be in one of your lambda definitions. Now this is an
area I don't know much about, and I don't know enought about the Java
code, but the error message says that two arguments are passed to a
method, where only one is expected.

In your code, I can't see any function where you explicitly pass two
arguments, so just a wild guess:

'collect' is defined on the Java side, expecting Java-Lambdas, and you
are passing it Ruby-Lambdas. Could it be, that this conversion doesn't
work right yet in JRuby?

What happens, if you directly execute

  number.stream.collect(...)

? If this brings up the same error message, I suspect that this is a bug
in JRuby and you could open a bug report for it.
3b8ad006781b1a3ab36dc76e2a05ce9c?d=identicon&s=25 Cris Shupp (cshupp)
on 2015-11-20 20:11
This topic is locked and can not be replied to.