what is the best way to get a sub HashMap based on a list of Keys?

30,371

Solution 1

With Java8 streams, there is a functional (elegant) solution. If keys is the list of keys to keep and map is the source Map.

keys.stream()
    .filter(map::containsKey)
    .collect(Collectors.toMap(Function.identity(), map::get));

Complete example:

    List<Integer> keys = new ArrayList<>();
    keys.add(2);
    keys.add(3);
    keys.add(42); // this key is not in the map

    Map<Integer, String> map = new HashMap<>();
    map.put(1, "foo");
    map.put(2, "bar");
    map.put(3, "fizz");
    map.put(4, "buz");

    Map<Integer, String> res = keys.stream()
        .filter(map::containsKey)
        .collect(Collectors.toMap(Function.identity(), map::get));

    System.out.println(res.toString());

Prints: {2=bar, 3=fizz}

EDIT add a filter for keys that are absent from the map

Solution 2

Yes there is a solution:

Map<K,V> myMap = ...;
List<K> keysToRetain = ...;
myMap.keySet().retainAll(keysToRetain);

The retainAll operation on the Set updates the underlying map. See java doc.

Edit Be aware this solution modify the Map.

Solution 3

With a help of Guava.

Suppose you have a map Map<String, String> and want to submap with a values from List<String> list.

Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "4");

final List<String> list = Arrays.asList("2", "4");

Map<String, String> subMap = Maps.filterValues(
                map, Predicates.in(list));

Update / Note: As @assylias mentioned in the comment, you will have O(n) when using contains(). So if you have large list, this could have huge impact in performance.

On the other side HashSet.contains() is constant time O(1), so if there is a possibility to have Set instead of List, this could be a nice approach (note that converting List to Set will cost O(n) anyway, so better not to convert :))

Solution 4

If you have Map m1 and List keys, then try following

Map m2 = new HashMap(m1);
m2.keySet().retainAll(keys);

Solution 5

Depending on your usage, this may be a more efficient implementation

public class MapView implements Map{
  List ak;
  Map map;
  public MapView(Map map, List allowableKeys) {
     ak = allowableKeys;
     map = map;
  }
  public Object get(Object key) {
    if (!ak.contains(key)) return null;
    return map.get(key);
  }
}
Share:
30,371
aregnier
Author by

aregnier

Updated on March 30, 2021

Comments

  • aregnier
    aregnier over 3 years

    I have a HashMap and I would like to get a new HashMap that contains only the elements from the first HashMap where K belongs to a specific List.

    I could look through all the keys and fillup a new HashMap but I was wondering if there is a more efficient way to do it?

    thanks