Tuesday, February 18, 2014

Map and Reduce methods in Java


For those of you, who have been using Scala for a while and now came back to Java its hard to accept the fact that there is no map and reduce methods available in Java. Lets say you want to write a function that takes a List, and returns the product (of multiplication) of all the Ints in the list. It will be similar to this:
public static int product(List seq){
 int product = 0;
 
 for(int i : seq){
  product *= i;
 }
 
 return product;
}

In Scala you can write the same code in one line:
val result = (1 to 10).fold(0)(_ * _)
How about a method that counts a letters of words in a sentence?
public Collection countWords(Collection words){
 
 Collection result = new ArrayList();
 
 for(String word : words){
  result.add(word.length());
 }
 
 return result;
}

Here is the same logic written in Scala:
words.map(_.length)
So is there a way to do map and reduce in Java?

Yes, that's how I solved this problem. First, you'll need two classes that represents high-order functions of one and two arguments.
public interface Function1 {
 
 public R run(IN param);

}

public interface Function2 {
 
 public R run(IN1 param1, IN2 param2);

}

Next, lets write our map and reduce methods.
public class CollectionUtils {

 public static  R fold(Collection sequence, R initialValue, Function2 action) {

  for (IN element : sequence) {
   initialValue = action.run(initialValue, element);
  }

  return initialValue;
 }

 public static  Collection map(Collection sequence, Function1 action) {
  
  Collection result = new ArrayList<>();

  for (IN element : sequence) {
   result.add(action.run(element));
  }

  return result;
 }

}

Now lets get back to our examples. Here is how you are going to do the product and word count examples with help of those methods. First you'll need two helper functions.
public class Functions {
 
        public static Function2 product() {

  return new Function2() {

   @Override
   public Integer run(Integer param1, Integer param2) {

    return param1 * param2;

   }

  };

 }
 
 public static Function1 countLetters() {

  return new Function1() {

   @Override
   public Integer run(String param1) {

    return param1.length();

   }

  };

 }
 
}
Finally, one-liner that does product of number of letters of all words in a sentence.
CollectionUtils.fold(CollectionUtils.map(Arrays.asList("The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"), Functions.countLetters()), 1, Functions.product());