Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Generics: How to populate a map from arrays?

I tried to write a utility method which would populate a map from a key and a value array, no matter what the data type of key and value would be.

  public static <K,V> Map<K,V> fillMap(Map<K,V> map, K[] keys, V[] values) {
    int l= keys.length;
    for (int i=0; i<l; i++)
      map.put(keys[i], values[i]);
    return map;
  }

Then I called the method with

    HashMap<Integer, String> map= new HashMap<>();
    Integer[] keys= IntStream.range(0, 12).boxed().toArray(Integer[]::new);
    String[] values= new String[] {"Jan","Feb","Mar","Apr","Mai","Jun",
                   "Jul","Aug","Sep","Okt","Nov","Dez"};
    map= MyUtil.fillMap(map, keys, values);

and received the error:
incompatible types: no instance(s) of type variable(s) K,V exist so that Map<K,V> conforms to HashMap<Integer,String>
All attempts to replace <K,V> by variations of <? extends Object> and the like failed so far.
How can this be fixed?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

Problem

In the code presented, we declare:

HashMap<Integer, String> map = new HashMap<>();

and

public static <K,V> Map<K,V> fillMap(Map<K,V> map, K[] keys, V[] values)

Hence, if we call

map = MyUtil.fillMap(map, keys, values);

we try to assing a Map<...> (returned by MyUtil::fillMap) to a HashMap<...>. This cannot work since a Map is not a HashMap.

Possible solutions

There are two ways that come to my mind to fix this issue:

  1. either change the type of map,
  2. or make the return-type of MyUtil::fillMap generic.

1. change the type of map:

We can change the type of map from HashMap<...> to Map<...>:

Map<Integer, String> map = new HashMap<>();
...
map = MyUtil.fillMap(map, keys, values);

Ideone demo


2. Make the return-type of MyUtil::fillMap generic:

By adding an additional generic parameter, we can make the concrete implementation of the return type generic as well:

public static void main(String[] args) {
  HashMap<Integer, String> map = new HashMap<>();
  final Integer[] keys = IntStream.range(0, 12).boxed().toArray(Integer[]::new);
  final String[] values = new String[] {"Jan", "Feb", "Mar", "Apr", "Mai", "Jun",
      "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"};
  map = MyUtil.fillMap(map, keys, values);
  System.out.println(map);
}

public static <K, V, M extends Map<K, V>> M fillMap(M map, K[] keys, V[] values) {
  final int l = keys.length;
  for (int i = 0; i < l; i++) {
    map.put(keys[i], values[i]);
  }
  return map;
}

Ideone demo


Bonus: state-free construction of the return-value

If it is not necessary to pass-in the concrete implementation of the map used to the method, I would propose a third option that creates the map to return within the method:

public static <K, V> Map<K, V> fillMap(K[] keys, V[] values) {
  return IntStream.range(0, keys.length)
      .boxed()
      .collect(Collectors.toMap(
          index -> keys[index],
          index -> values[index]));
}

Ideone demo

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading