user1317512
user1317512

Reputation: 35

Java 8 Map KeySet Stream not working as desired for use in Collector

I have been trying to learn Java 8's new functional interface features, and I am having some difficulty refactoring code that I have previously written.

As part of a test case, I want to store a list of read names in a Map structure in order to check to see if those reads have been "fixed" in a subsequent section of code. I am converting from an existing Map> data structure. The reason why I am flattening this datastructure is because the outer "String" key of the original Map is not needed in the subsequent analysis (I used it to segregate data from different sources before merging them in the intermediate data). Here is my original program logic:

public class MyClass {
  private Map<String, Map<String, Short>> anchorLookup;
  ...
  public void CheckMissingAnchors(...){
    Map<String, Boolean> anchorfound = new HashMap<>();

    // My old logic used the foreach syntax to populate the "anchorfound" map
    for(String rg : anchorLookup.keySet()){
      for(String clone : anchorLookup.get(rg).keySet()){
        anchorfound.put(clone, false);
      }
    }
    ...
    // Does work to identify the read name in the file. If found, the boolean in the map
    // is set to "true." Afterwards, the program prints the "true" and "false" counts in 
    // the map
   }
}

I attempted to refactor the code to use functional interfaces; however, I getting errors from my IDE (Netbeans 8.0 Patch 2 running Java 1.8.0_05):

public class MyClass {
  private Map<String, Map<String, Short>> anchorLookup;
  ...
  public void CheckMissingAnchors(...){

    Map<String, Boolean> anchorfound = anchorLookup.keySet()
      .stream()
      .map((s) -> anchorlookup.get(s).keySet())  // at this point I am expecting a 
      // Stream<Set<String>> which I thought could be "streamed" for the collector method
      // ; however, my IDE does not allow me to select the "stream()" method
      .sequential() // this still gives me a Stream<Set<String>>
      .collect(Collectors.toMap((s) -> s, (s) -> false);
      // I receive an error for the preceding method call, as Stream<Set<String>> cannot be 
      // converted to type String
    ...
   }
}

Is there a better way to create the "anchorfound" map using the Collection methods or is the vanilla Java "foreach" structure the best way to generate this data structure?

I apologize for any obvious errors in my code. My formal training was not in computer science but I would like to learn more about Java's implementation of functional programming concepts.

Upvotes: 3

Views: 8435

Answers (2)

Stuart Marks
Stuart Marks

Reputation: 132590

Eran's suggestion of flatMap is a good one, +1.

This can be simplified somewhat by using Map.values() instead of Map.keySet(), since the map's keys aren't used for any other purpose than to retrieve the values. Streaming the result of Map.values() gives a Stream<Map<String,Short>>. Here we don't care about the inner map's values, so we can use keySet() to extract the keys, giving a Stream<Set<String>>. Now we just flatMap these sets into Stream<String>. Finally we send the results into the collector as before.

The resulting code looks like this:

public class MyClass {
    private Map<String, Map<String, Short>> anchorLookup;

    public void checkMissingAnchors() {
        Map<String, Boolean> anchorfound = anchorLookup.values().stream()
            .map(Map::keySet)
            .flatMap(Set::stream)
            .collect(Collectors.toMap(s -> s, s -> false));
    }
}

Upvotes: 5

Eran
Eran

Reputation: 394106

I believe what you need is a flatMap.

This way you convert each key of the outer map to a stream of the keys of the corresponding inner map, and then flatten them to a single stream of String.

public class MyClass {
  private Map<String, Map<String, Short>> anchorLookup;
  ...
  public void CheckMissingAnchors(...){

    Map<String, Boolean> anchorfound = anchorLookup.keySet()
      .stream()
      .flatMap(s -> anchorlookup.get(s).keySet().stream())
      .collect(Collectors.toMap((s) -> s, (s) -> false);
    ...
  }
}

Upvotes: 5

Related Questions