0

I have a HashMap like Map<Long, List<String>> map = .... and

also I have a list of key like List<Long> keyList = Arrays.asList(1L, 3L, 10L);

I want to extract the List<String> by these keyList's and generate a single List<String> using lambda expression.

2 Answers 2

5

Try this:

List<String> values = keyList.stream()    // for each key
    .map(map::get)                        // convert keys to their values
    .flatMap(List::stream)                // stream the lists as one stream
    .collect(Collectors.toList());        // as a single list of String
Sign up to request clarification or add additional context in comments.

Comments

3

Another approach would be to use reduce instead of flatMap, collect:

import java.util.*;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;

class Main {

  public static void main(String[] args) {
    final Map<Long, List<String>> map = createTestMap();
    final List<Long> keyList = asList(1L, 3L, 10L);
    final List<String> resultList = keyList.stream()
        .map(map::get)
        .map(ArrayList::new)  // need to create new lists not to corrupt the existing ones
        .reduce((left, right) -> { left.addAll(right); return left;})  // accumulate the list
        .orElse(new ArrayList<>());  // empty list if nothing was found
    System.out.println(resultList);
  }

  private static Map<Long, List<String>> createTestMap() {
    final Map<Long, List<String>> map = new HashMap<>();
    final List<String> oneList = asList("1 love", "1 peace");
    final List<String> threeList = singletonList("3 words");
    final List<String> tenList = emptyList();
    final List<String> ninetyList = singletonList("99 problems");
    map.put(1L, oneList);
    map.put(3L, threeList);
    map.put(10L, tenList);
    map.put(90L, ninetyList);
    return map;
  }
}

4 Comments

Thanks, exactly this.
Even if you copy each element to a new ArrayList, this is an abuse of reduce. Using a correct Mutable reduction, is not harder than your .map(ArrayList::new) .reduce((left, right) -> { left.addAll(right); return left;}) .orElse(new ArrayList<>()). You can use either collect(ArrayList::new, List::addAll, List::addAll) or .flatMap(List::stream) .collect(Collectors.toList()), both being simpler, shorter and more efficient than the abuse of reduce
After reading about Mutable reduction, I also think that it would be more proper to apply collect. However, the proposed approach is an alternative, and why could it abuse the reduce function?
An ordinary reduction function should not modify its incoming values. You are fixing this by copying every list into a new ArrayList in the preceding map step, which is not only very inefficient, but creating a non-obvious dependency between the terminal operation and the preceding intermediate operation. How many readers would understand this dependency without the clarifying comment? Or, in other words, doing the intended thing is not always sufficient to be correct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.