Java: create copy of ArrayList of HashMaps but include only certain keys



Consider the following data structures:

ArrayList<HashMap<String, String>> entries = new ArrayList<>();

ArrayList<String> keyNamesToInclude = new ArrayList<>();

This code creates a copy of entries, but with hashmaps only including the keys in keyNamesToInclude:

ArrayList<HashMap<String, String>> copies = new ArrayList<>();

for (HashMap<String, String> entry: entries) {
  HashMap<String, String> copy = new HashMap<>();
  for (String keyName: keyNamesToInclude) {
    copy.put(keyName, entry.get(keyName));
  }
  copies.add(copy);
}

How would one create this with Streams in a functional way?

Answer

It is better to convert keyNamesToInclude into Set to facilitate lookup of the keys.

Then use List::stream to get Stream<HashMap> and for each map get filtered stream of its entries re-collected into a new map and list accordingly.

Set<String> keys = new HashSet<>(keyNamesToInclude); // removes possible duplicates
List<Map<String, String>> copies = entries.stream() // Stream<HashMap>
    .map(m -> m.entrySet()
        .stream()
        .filter(e -> keys.contains(e.getKey()))
        .collect(Collectors.toMap(
            Map.Entry::getKey, Map.Entry::getValue
        ))
    )
    .collect(Collectors.toList());

If it is very important to have concrete implementations of List and Map in copies, casting or special forms of collectors may be needed even though Collectors.toList() returns ArrayList and Collectors.toMap returns HashMap:

// casting
ArrayList<HashMap<String, String>> copies2 = (ArrayList) entries.stream() // Stream<HashMap>
    .map(m -> (HashMap<String, String>) m.entrySet()
        .stream()
        .filter(e -> keys.contains(e.getKey()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
    )
    .collect(Collectors.toList());

// special collectors
// toMap(keyMapper, valueMapper, mergeFunction, mapFactory)
// toList -> toCollection(ArrayList::new)
ArrayList<HashMap<String, String>> copies3 = entries.stream() // Stream<HashMap>
    .map(m -> m.entrySet()
        .stream()
        .filter(e -> keys.contains(e.getKey()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, HashMap::new))
    )
    .collect(Collectors.toCollection(ArrayList::new));


Source: stackoverflow