Java 8 – This ain’t your grandpa’s Java

Java 8 adds support for bulk data operations (JEP 107) in the Collections library. It is unlikely every forms of bulk data operations will be added to the retrofitted Collections framework, but the language designers are making every possible effort in accommodating general use cases of “filter/map/reduce for Java”. Libraries should make use of parallelization on multi-core platforms in order to perform massive parallelizable operations and this means literally kicking-in your cores to its fullest potential. This area of OpenJDK is undergoing heavy changes, it is possible these APIs are changed or improved before milestone 6 when JDK 8 is officially feature complete. But, they are stable enough to give it a spin today. You can grab the lambda build from here. I have been using the latest Intellij 12 preview for my hands on with the new features in Java 8. It actually supports most of the new Java 8 languauge features and intellisense rocks.

The Streams proposal is well thought of and brings the much sought after functional power to Java and you won’t regret upgrading from Java 6 -> Java 8, if you are still hanging around with Java 6 in that time frame. This one is just a preview of what is coming in Java 8.

Let us implement a method to check if a keyword list exists in a tweet. Here is one way to implement it in plain old Java.


public class TweetUtil {
public static void main(String[] args) {
final List<String> keywords = Arrays.asList("brown", "fox", "dog", "pangram");
final String tweet = "The quick brown fox jumps over a lazy dog. #pangram http://www.rinkworks.com/words/pangrams.shtml&quot;;
exists(keywords, tweet);
}
public static boolean exists(List<String> keywords, String tweet) {
for (String keyword : keywords) {
if (tweet.contains(keyword)) return true;
}
return false;
}
}

view raw

TweetUtil.java

hosted with ❤ by GitHub

Now, that I am taking the Functional programming course, I would like to compare it with Scala, the mature functional language on the JVM. In Scala, this can be implemented as one liners and there are couple different ways to do this, one using exists and other using the foldLeft function, although exists is my favorite as it is clean and less cryptic.


object TweetUtil {
def main(args: Array[String]) {
val keywords = List("brown", "fox", "dog", "pangram")
val tweet = "The quick brown fox jumps over a lazy dog. #pangram http://www.rinkworks.com/words/pangrams.shtml&quot;
keywords.exists(tweet.contains)
(keywords.foldLeft(false)( _ || tweet.contains(_) ))
}
}

view raw

TweetUtil.scala

hosted with ❤ by GitHub

I can understand why functional programmers are more obsessed with “small is beautiful” theme and hate outright verbosity. Now that all dust has settled with Java’s once uncertain future and stalled innovation, its new steward Oracle is betting on to bring back its lost glory in round 2 and you see that happening with Java 8. Finally, the language brings freshness to Java camp in offering functional constructs to Java and make functional programming in Java a breeze. Here is a teaser:

https://gist.github.com/3847359.js

Isn’t nice to have a one liners in Java too? Java is close, but could reduce some noise as you have to call stream() on the Collection which might seem unnecessary. Stream interface supports a fold operation very similar to Scala’s foldLeft function and implements using a similar approach as of Scala.

Now, let us take this example further, filter tweets matching keywords. This was inspired by Week 3 assignment from the “Functional Programming Principles in Scala” course which btw I am enjoying it so far. Again, you can see Java makes it a cut. I am trying to keep it not being a spoiler.


List<String> google = Arrays.asList("android", "Android", "galaxy", "Galaxy", "nexus", "Nexus");
List<String> apple = Arrays.asList("ios", "iOS", "iphone", "iPhone", "ipad", "iPad");
List<Tweet> tweets = TweetReader.fromTweetSet(TweetReader.allTweets);
// Using anyMatch and method reference
List<Tweet> googleTweets = tweets.stream().filter(t -> (google.stream().anyMatch(t.text::contains))).collect(toList());
List<Tweet> appleTweets = tweets.stream().filter(t -> (apple.stream().anyMatch(t.text::contains))).collect(toList());
// Using reduce
List<Tweet> googleTweets2 = tweets.stream().filter(t -> google.stream().reduce(false, (Boolean b, String keyword) -> b || t.text.contains(keyword), (l, r) -> l | r)).collect(toList());
List<Tweet> appleTweets2 = tweets.stream().filter(t -> apple.stream().reduce(false, (Boolean b, String keyword) -> b || t.text.contains(keyword), (l, r) -> l | r)).collect(toList());

In this example, you notice that you have to use stream() as well as into() operations to get the result of stream processing. I believe this is a limitation with the iteration 2 Streams implementation which was referred as “the bun problem” that we have to live with.

Streams in Java 8 is making implementing bulk operations a no brainer. While it involves some effort in learning the new concepts, it opens up a new dimension to library developers. Thanks to JSR 335 which brings Java up to speed with other cool languages out there in the planet.

For fun, I want to toss in a new exists() API to the Stream interface so I could use it to filter on my keywords list. This can be done with the help of extension methods, another new concept in Java 8.


package java.util.stream;
import java.util.function.Predicate;
public interface MyStream<T> extends Stream<T> {
default boolean exists(Predicate<? super T> predicate) {
return anyMatch(predicate);
}
}

view raw

MyStream.java

hosted with ❤ by GitHub


package java.util.stream;
import java.util.Spliterator;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class MyReferencePipeline<T, U> extends ReferencePipeline<T, U> implements MyStream<U> {
public MyReferencePipeline(Supplier<? extends Spliterator<?>> source, int sourceFlags, boolean parallel) {
super(source, sourceFlags, parallel);
}
public MyReferencePipeline(Spliterator<?> source, int sourceFlags, boolean parallel) {
super(source, sourceFlags, parallel);
}
@Override
public boolean exists(Predicate<? super U> predicate) {
return evaluate(MatchOps.makeRef(predicate, MatchOps.MatchKind.ANY));
}
}

Basically, I am delegating the implementation to anyMatch() for simplicity as both reveal the same purpose. Here is a custom List implementation that exposes this API on the collection. This is a primitive implementation, which btw I am trying to keep this updated with the recent library changes. I hope there is a better way to do this, but the point is, it enables developers to extend the Streams framework and fill in special cases.


package java.util.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class MyList<E> extends ArrayList<E> {
public MyList(int initialCapacity) {
super(initialCapacity);
}
public MyList() {
super();
}
public MyList(Collection<? extends E> c) {
super(c);
}
@Override
public MyStream<E> stream() {
return new MyReferencePipeline<>(Arrays.spliterator((E[]) this.toArray(), 0, this.size()), StreamOpFlag.IS_SIZED | StreamOpFlag.IS_ORDERED, false);
}
@Override
public Stream<E> parallelStream() {
return new MyReferencePipeline<>(Arrays.spliterator((E[]) this.toArray(), 0, this.size()), StreamOpFlag.IS_SIZED | StreamOpFlag.IS_ORDERED, true);
}
}

view raw

MyList.java

hosted with ❤ by GitHub

exists() variant of the code is shown below.


// Run with -Xbootclasspath/p:target/classes as ReferencePipeline access is now package local, so use this trick
List<Tweet> googleTweets3 = tweets.stream().filter(tweet -> (new MyList<>(google).stream().exists(tweet.text::contains))).collect(toList());
List<Tweet> appleTweets3 = tweets.stream().filter(tweet -> (new MyList<>(apple).stream().exists(tweet.text::contains))).collect(toList());

There are several new concepts in the library that requires functional mindset to fully benefit from it. You can switch these Streams to use a parallel version, by using parallel() instead of stream() on the Collection. Stream operations are classified as lazy and eager based on their implementation.
I hope to uncover some of these new concepts in the coming months. I wish function types made into Java 8, but that is different story for another day. Enjoy functional programming in Java!

I have covered Lambdas in the past and this excellent JavaZone talk by Angelika Langer should bring you up to speed with the current state of affairs with Lambdas in Java 8. You can also find Maurice Naftalin’s LambdaFAQ useful.

Lambdas in Java 8 from JavaZone on Vimeo.

Update (10/20/2012) : Updated samples to use method reference and built with the latest Lambda build 61. Clarified the bun-problem with streams proposal.
Update (11/20/2012) : Updated MyList implementation to compile with the latest API changes and much cleaner implementation.
Update (2/3/2013) : Updated code to compile with the latest lambda build 75.
Update (6/15/2013) : Updated code to compile with the latest lambda build 94.

2 thoughts on “Java 8 – This ain’t your grandpa’s Java

  1. Man, you shouldn’t public answers before deadline. I can’t see statement that your code comes from coursera.org . Your posts is linked from java.dzone.com and I’m almost sure your code has too many hints (so someone could lost fun from implementing own solution). Edit it pls.

    Like

    1. I did mention that examples were inspired from progfun course. I was careful in not being a spoiler. I updated the post to be more generic. Hope this helps!

      Like

Leave a comment