Wednesday, September 5, 2018

Using Java 8 Streams API with a C# mind

I did a coding dojo with my team the other day. It went quite well until the pair at the keyboard wanted to sum a list of integers with Java 8 streams. This should only take a few seconds, right? The pair got stuck for almost 5 minutes. The whole dojo group tried to help, even though we have the rule that the other dojo participants should be quiet. But everybody got nervous, especially the poor coworker at the keyboard. "Let's do it with a for loop", he said multiple times.

Most of us where experienced Java developers. How could we struggle with such a simple problem? I have to say that we all where not much experienced with Java 8 streams as we cannot use it much in our daily work. In fact our codebase uses Java 8 but the legacy code we work with makes heavy use of checked exceptions. This makes it hard to work with lambdas. As streams depend on lambdas, there is not much streams for us.

But back to our integer summing problem. Coming from .NET where I can write ...

 // C# version  
 products  
 .Select(p -> p.UnitsInStock)  
 .Sum();   

... I would have expected something similar in Java. Maybe something like this:

 products.stream()  
 .map(p -> p.unitsInStock)  
 .sum();   

But the code above doesn't compile, right? There is no sum method here.

It turns out that the reason why Java doesn't meet my (C# inluenced) intensions here are two fold. The first problem is that Java doesn't support generic lists for primitive types. As the streams API works only with generic lists we only can stream on boxed primitives. To be able to sum the integers we need to map from Integer to int first:

 products.stream()  
 .map(p -> p.unitsInStock)  
 .mapToInt(i -> i.intValue())  
 .sum();  

or shorter:

  products.stream()   
  .map(p -> p.unitsInStock)   
  .mapToInt(i -> i)   
  .sum();   

It's not too bad. Better than a for loop for sure. But I prefer the C# version shown above, as it is more compact.

The second thing that struggles me is that I often have to use a helper class like Collectors or Comparator when working with streams. I need to know that these classes exist, otherwise I'm stuck. In C# everything is accessible via the LINQ operators on the IEnumerable interface. This makes the API very easy to use.

But even though the stream API could be more intentional, I like using it. When I moved from .NET to Java two years ago I had to use Java 7. To have no lambdas and no LINQ alternative was terrible. Now that I'm on Java 8 with lambdas and streams I think I'm as productive as I was with C#. I'm sure I will soon get addicted to streams