Don’t Shoehorn Java 8 Streams Into Every Problem

in #java88 years ago

 

Learn more about how the Java language, tools and frameworks have been the foundation of countless enterprise systems, brought to you in partnership with Salesforce.

With Java 8 being mainstream now, people start using Streams for everything, even in cases where that’s a bit exaggerated (a.k.a. completely nuts, if you were expecting a hyperbole here). For instance, take mykong’s article here, showing how to collect a Map’s entry set stream into a list of keys and a list of values: http://www.mkyong.com/java8/java-8-convert-map-to-listThe code posted on mykong.com does it in two steps:

 

package com.mkyong.example;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ConvertMapToList {
   public static void main(String[] args) {
       Map<Integer, String> map = new HashMap<>();
       map.put(10, "apple");
       map.put(20, "orange");
       map.put(30, "banana");
       map.put(40, "watermelon");
       map.put(50, "dragonfruit");
       System.out.println("\n1. Export Map Key to List...");
       List<Integer> result = map.entrySet().stream()
               .map(x -> x.getKey())
               .collect(Collectors.toList());
       result.forEach(System.out::println);
       System.out.println("\n2. Export Map Value to List...");
       List<String> result2 = map.entrySet().stream()
               .map(x -> x.getValue())
               .collect(Collectors.toList());
       result2.forEach(System.out::println);
   }
}


 This is probably not what you should do in your own code. First off, if you’re OK with iterating the map twice, the simplest way to collect a map’s keys and values would be this:

List<Integer> result1 = new ArrayList<>(map.keySet());
List<String> result2 = new ArrayList<>(map.values());

There’s absolutely no need to resort to Java 8 streams for this particular example. The above is about as simple and speedy as it gets.

Don’t shoehorn Java 8 Streams into every problem

But if you really want to use streams, then I would personally prefer a solution where you do it in one go. There’s no need to iterate the Map twice in this particular case. For instance, you could do it by using jOOλ’s Tuple.collectors() method, a method that combines two collectors into a new collector that returns a tuple of the individual collections.Code speaks for itself more clearly than the above description. Mykong.com’s code could be replaced by this: 

 

Tuple2<List<Integer>, List<String>> result = 
map.entrySet()
   .stream()
   .collect(Tuple.collectors(
       Collectors.mapping(Entry::getKey, Collectors.toList()),
       Collectors.mapping(Entry::getValue, Collectors.toList())
   ));

The only jOOλ code put in place here is the call to Tuple.collectors(), which combines the standard JDK collectors that apply mapping on the Map entries before collecting keys and values into lists.When printing the above result, you’ll get:

([50, 20, 40, 10, 30], [dragonfruit, orange, watermelon, apple, banana])

i.e. a tuple containing the two resulting lists.Even simpler, don’t use the Java 8 Stream API, use jOOλ’s Seq (for sequential stream) and write this shorty instead:

Tuple2<List<Integer>, List<String>> result = 
Seq.seq(map)
  .collect(
       Collectors.mapping(Tuple2::v1, Collectors.toList()),
       Collectors.mapping(Tuple2::v2, Collectors.toList())
  );

Where Collectable.collect(Collector, Collector) provides awesome syntax sugar over the previous example.