0

I got a scenario like the following:

Map1 - Map<String, Map<String,List<Vo>>>  
Map2 - Map<String, Set<String>  

Is it possible to set the same have a same key reference for the above 2 Maps like the following?

Map<String, Collection<?>  mapCommon=new HashMap<String, Collection<?>();  

Can anyone please give some idea about how to set this? edit: yes same reference

user1514499
  • 762
  • 7
  • 26
  • 63

3 Answers3

1

What you want to do is impossible because Set and Map do not share any common implementation or super class except Object. You can see it in the official documentation :

You could do a Map<String, Object> but I strongly not advise you to doing that. How could you know if your object is a map or a set ? It is not possible to do that properly.

In my opinion, the best solution you have is to create a new class to wrap your two collections :

public class YourWrapper {
    Map<String, Map<String,List<Vo>>> a; 
    Map<String, Set<String> b;
    // getter setter etc...
}

After that you can create your collection :

Map<String, YourWrapper> myMap = new HashMap<String, YourWrapper>();
Thomas Betous
  • 4,633
  • 2
  • 24
  • 45
1

If I understand you would want to have the same key to be used for various different types of values.

Why not have a new Class itself that would consists of maps, sets, whose instances could be used as values

class MyClass {

     private Map<String, List<Vo>> theMap;
     private Set<String> theSet;
     ...
     ...   // have its own getters and setters

}

And then you can have your top level map defined like this

Map<String, MyClass> myMainMap = new HashMap<String, MyClass>();

Or as an alternative have a tuple

You can check this link further to see how that is done.

Community
  • 1
  • 1
Kamal Kunjapur
  • 8,547
  • 2
  • 22
  • 32
1

You are touching here two interesting elements.

Firstly - Map does not belong to Collection. List and Set do belong, but Map is a different one even though it shares some commonalities with Lists and Sets.

Secondly - Mixing the types into one commonMap the way you are trying is doable but it should be avoided as it is generally not considered as best practice. The problem we are dealing with is caused by type erasure. Once compiler compiles the code - it does not pass any information about generic types hold by Map or Set. Effectively your Map<String, List<Vo>> becomes raw-type Map<?> in the compiled code. The problem with that is casting back original values. The compiler will not allow you to check the instance if it is Map<String, List<Vo>> or Set<String>. The fllowing piece of code will fail:

public static void processElement(Object commonMapObjectEitherMapOrSet) {
    if (commonMapObjectEitherMapOrSet instanceof Map<String, List<Vo>>) {
        //...
    }
}

Error: Cannot perform instanceof check against parameterized type Map>. Use the form Map instead since further generic type information will be erased at runtime

The possible workaround would be to forget about generics and check if the instance is a raw-type Set or Map. The code below shows how check if Object is either Map or Set.

public static void processElement(Object commonMapObjectEitherMapOrSet) {
    if (commonMapObjectEitherMapOrSet instanceof Map) {
        System.out.println("Got map; but types held in the map are not known due to type-erasure");

        // This is where things will get messy as you will get warnings:
        Map<String, List<Vo>> map = (Map<String, List<Vo>>) commonMapObjectEitherMapOrSet;
        // ...
    }

    if (commonMapObjectEitherMapOrSet instanceof Set) {
        System.out.println("Got set; but types held in the set are not known due to type-erasure");

        // This is where things will get messy as you will get warnings:
        Set<String> set = (Set<String>) commonMapObjectEitherMapOrSet;
        // ...

    }
}

The problem with the above is casting the value from your commonMap back to your desired types ie. Map<String, List<Vo>> and Set<String>. The compiler won't be able to check if the casting is correct and will issue a warning. You can technically Suppress the warning with (@SuppressWarnings("unchecked") annotation ) but this may not be the best thing to do.

At this stage - it makes sense to consider whether or not to create your own specialized class to manage different types. Back to your original question - to answer it I am posting the code that maps things to the common map:

package stackoverflow;

import java.util.*;

class Vo {}

public class MultipleRefs {
    public static void main(String[] args) {

        Map<String, List<Vo>> mapVo = new HashMap<>();
        Set<String> set = new HashSet<>();


        Map<String, Object> commonMap = new HashMap<>();

        //commonMap.put("a", Map) 
        commonMap.put("mapVoOne", mapVo);
        commonMap.put("setOne", set);

        commonMap.forEach((key, value) -> processElement(value));
    }

    public static void processElement(Object commonMapObject) {
        if (commonMapObject instanceof Map) {
            System.out.println("Got map; but types held in the map are not known due to type-erasure");

            // This is where things will get messy:
            Map<String, List<Vo>> map = (Map<String, List<Vo>>) commonMapObject;

            System.out.println("  processElement prints map: " + map);
        }

        if (commonMapObject instanceof Set) {
            System.out.println("Got set; but types held in the set are not known due to type-erasure");

            // This is where things will get messy:
            Set<String> set = (Set<String>) commonMapObject;

            System.out.println("  processElement prints set: " + set);
        }
    }
}
Witold Kaczurba
  • 9,845
  • 3
  • 58
  • 67