Newbie generic parameter qu estion... <T> - java

Okay so can i achive this somehow:
String myString = "someString";
Class myClass = myString.getClass();
HashMap<mClass, Integer> = new HashMap<myClass, Integer>();
So i would like to create a new hashmap, with class type of the key of my variables like Integer or String...

This is not possible. I'll walk you through the possibilities.
You could create a helper method, using generics. This will work because of all generics are compiled into simple Objects.
public static <T> Map<T, Integer> createMap(Class<T> cl)
{
return new HashMap<T, Integer>();
}
Now, you could use it like this:
Map<String, Integer> map = createMap(String.class);
However, this will require you to know what T is at compile time. So this won't work:
String str = "Test";
Class cl = str.getClass();
Map<String, Integer> map = createMap(cl); // Doesn't compile.
So, to conclude, this helper method isn't worth anything, because you could simply write:
Map<String, Integer> map = new HashMap<String, Integer>();

Due to type erasure this would not work.
A possible (but more verbose way) is to create a factory method that returns a Map based on the passed argument, eg:
MapFactory.create(String.class);
EDIT: In answer to #millimoose comment about this being not different from direct instantiation (which is true):
You could try to implement your own Map or decorate or extend the HashMap implementation so that it retains type information.

Related

java - to set multiple value in a map

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
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);
}
}
}
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.
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 :
Javadoc Map
Javadoc Set
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>();

Does Hashtable get method returns more than one value?

I was converting some code from java to C#, I encountered ArrayList<Integer> values = hashtable.get(h);. Question aroused Does Hashtable get method returns more than one value?
A HashTable returns one value. If that value happens to be an object of type Collection, then that one value will point to several other values.
For example
HashTable<String, ArrayList<Integer>> table = new HashTable<String, ArrayList<Integer>>();
// Populate it with values.
ArrayList<Integer> value = table.get("KEY");
How is this possible?
Simple. Java Generics. This is where you declare a Generic type in a class, and you define it's type at run time. For example:
public class Test<T>
{
private T instance;
public Test(T instance)
{
this.instance = instance;
}
}
That means you can declare this class any way you want.
Test<String> test = new Test<String>();
Test<Integer> test2 = new Test<Integer>();
And the type of instance will be whatever you declare it as.
And because T defaults to type Object, you can even put a Collection in there.
Test<ArrayList<String>> test3 = new Test<ArrayList<String>>();
An ArrayList is one value (an arraylist) by itself
HashTable<something, ArrayList<Integer>> hashtable = new HashTable<something, ArrayList<Integer>>();
So it's gonna map the "something" to an arraylist of integers (i.e. a list)
The return type of get() method is Object. So it is a single Object. But the type can be a List or Any Class in Java.
So the returning Object purely depends on What you inserted before.
If you wold like to have many values for one key use Guava => Multimap
Documentation:
http://guava-libraries.googlecode.com/svn-history/r13/trunk/javadoc/com/google/common/collect/Multimap.html
It does only return a list of values if you put that list in the map (under the certain key).
Map<String, List<Object>> map = new HashMap<>();
... // init map
List<Object> list = map.get(KEY);
but
Map<String, Object> map = new HashMap<>();
map.put(KEY, obj1);
map.put(KEY, obj2);
Object obj = map.get(KEY);

How to define Map contents on initialisation? [duplicate]

This question already has answers here:
How can I initialise a static Map?
(43 answers)
Closed 6 years ago.
I was just wondering if it is possible to define the contents of a Map Object on initialisation.
For example, an array can be created, as:
new String[] {“apples”, “bananas”, “pears”}
So, I was wondering if there is something similar we can do for maps.
You can, sort of, using this syntax trick:
Map<String,String> map = new HashMap<String,String>() {{
put("x", "y");
put("a", "b");
}};
Not very pleasant, though. This creates an anonymous subclass of HashMap, and populates it in the instance initializer.
If your Map is going to be immutable after creation and you don't mind adding a dependency, Guava offers some nice fluent syntax:
Map<K,V> aMap = ImmutableMap.<K,V>builder().put(key0, val0).put(key1,val1).build();
If you're feeling really exotic, Scala has syntax exactly like what you want and is interoperable with other Java code:
val aMap = Map("a"->0, "b"->1)
Note that the Scala compiler will infer the Map generic type is from String to Int, based on what you put in it, though you can explicitly specify it as well.
However, if this is just a one-off, I'd go with the initializer-based syntax. Both the Guava library and Scala language have a lot else to recommend them, but learning a whole new library/language might be overboard.
You can use initializer blocks:
class Foo {
//using static initializer block
static Map<String,String> m1 = new HashMap<String,String>();
static {
m1.put("x","y");
m1.put("a","b");
}
//using initializer block
Map<String,String> m2 = new HashMap<String,String>();
{
m2.put("x","y");
m2.put("a","b");
}
}
Something very hacky..can be improved, but this is just a direction:
Define a static helper to convert an object array to a map of this type:
public static<K,V> Map<K, V> fromArray(Object[] anObjArray){
int size = anObjArray.length;
Map<K, V> aMap = new HashMap<K, V>();
for (int i=0;i<=size/2;i=i+2){
K key = (K)anObjArray[i];
V value = (V)anObjArray[i+1];
aMap.put(key, value);
}
return aMap;
}
then you can create a map using this:
Map<Integer, String> aMap = MapUtils.<Integer, String>fromArray(new Object[]{1, "one", 2,"two"});
I would personally second Gauva builder suggestion from #Carl though :-)

Java generics parameters with base of the generic parameter

I am wondering if there's an elegant solution for doing this in Java (besides the obvious one - of declaring a different/explicit function. Here is the code:
private static HashMap<String, Integer> nameStringIndexMap
= new HashMap<String, Integer>();
private static HashMap<Buffer, Integer> nameBufferIndexMap
= new HashMap<Buffer, Integer>();
// and a function
private static String newName(Object object,
HashMap<Object, Integer> nameIndexMap){
....
}
The problem is that I cannot pass nameStringIndexMap or nameBufferIndexMap parameters to the function. I don't have an idea about a more elegant solution beside doing another function which explicitly wants a HashMap<String, Integer> or HashMap<Buffer, Integer> parameter.
My question is:
Can this be made in a more elegant solution/using generics or something similar?
Thank you,
Iulian
You could make your function generic too:
private static <E extends Object> String newName(E object,
HashMap<E, Integer> nameIndexMap){
....
}
This bounds the two parameters of the function together, so for a HashMap<String, Integer> you can only pass String instances as first parameter. This may or may not be what you exactly want: if you only want to get elements from the map, Jon's solution is simpler, but if you want to add this object to the map, this one is the only choice.
You want something like this:
private static String newName(Object object,
HashMap<? extends Object, Integer> nameIndexMap) {
....
}
or (as pointed out in the comments)
private static String newName(Object object,
HashMap<?, Integer> nameIndexMap) {
....
}
That will stop you from putting anything into the map, because you couldn't guarantee to get the key right - but you can get things out of the map and guarantee they'll be integers.
Note that this version doesn't make the method generic - which means it's simpler, but it doesn't provide the same type safety that Peter's version does, in that you can't guarantee that object is of the right type. Each approach has its pros and cons - use whatever is most appropriate based on the body of the method. (If you need to put an entry into the map, Peter's approach is definitely better.)

What is the difference between ? and Object in Java generics?

I'm using Eclipse to help me clean up some code to use Java generics properly. Most of the time it's doing an excellent job of inferring types, but there are some cases where the inferred type has to be as generic as possible: Object. But Eclipse seems to be giving me an option to choose between a type of Object and a type of '?'.
So what's the difference between:
HashMap<String, ?> hash1;
and
HashMap<String, Object> hash2;
An instance of HashMap<String, String> matches Map<String, ?> but not Map<String, Object>. Say you want to write a method that accepts maps from Strings to anything: If you would write
public void foobar(Map<String, Object> ms) {
...
}
you can't supply a HashMap<String, String>. If you write
public void foobar(Map<String, ?> ms) {
...
}
it works!
A thing sometimes misunderstood in Java's generics is that List<String> is not a subtype of List<Object>. (But String[] is in fact a subtype of Object[], that's one of the reasons why generics and arrays don't mix well. (arrays in Java are covariant, generics are not, they are invariant)).
Sample:
If you'd like to write a method that accepts Lists of InputStreams and subtypes of InputStream, you'd write
public void foobar(List<? extends InputStream> ms) {
...
}
By the way: Joshua Bloch's Effective Java is an excellent resource when you'd like to understand the not so simple things in Java. (Your question above is also covered very well in the book.)
Another way to think about this problem is that
HashMap<String, ?> hash1;
is equivalent to
HashMap<String, ? extends Object> hash1;
Couple this knowledge with the "Get and Put Principle" in section (2.4) from Java Generics and Collections:
The Get and Put Principle: use an
extends wildcard when you only get
values out of a structure, use super
wildcard when you only put values into
a structure, and don't use a wildcard
when you both get and put.
and the wild card may start making more sense, hopefully.
It's easy to understand if you remember that Collection<Object> is just a generic collection that contains objects of type Object, but Collection<?> is a super type of all types of collections.
The answers above covariance cover most cases but miss one thing:
"?" is inclusive of "Object" in the class hierarchy. You could say that String is a type of Object and Object is a type of ?. Not everything matches Object, but everything matches ?.
int test1(List<?> l) {
return l.size();
}
int test2(List<Object> l) {
return l.size();
}
List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1); // compiles because any list will work
test1(l2); // compiles because any list will work
test2(l1); // fails because a ? might not be an Object
test2(l2); // compiled because Object matches Object
You can't safely put anything into Map<String, ?>, because you don't know what type the values are supposed to be.
You can put any object into a Map<String, Object>, because the value is known to be an Object.
Declaring hash1 as a HashMap<String, ?> dictates that the variable hash1 can hold any HashMap that has a key of String and any type of value.
HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();
All of the above is valid, because the variable map can store any of those hash maps. That variable doesn't care what the Value type is, of the hashmap it holds.
Having a wildcard does not, however, let you put any type of object into your map. as a matter of fact, with the hash map above, you can't put anything into it using the map variable:
map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");
All of the above method calls will result in a compile-time error because Java doesn't know what the Value type of the HashMap inside map is.
You can still get a value out of the hash map. Although you "don't know the value's type," (because you don't know what type of hash map is inside your variable), you can say that everything is a subclass of Object and, so, whatever you get out of the map will be of the type Object:
HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.
myMap.put("ABC", 10);
HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).
System.out.println(output);
The above block of code will print 10 to the console.
So, to finish off, use a HashMap with wildcards when you do not care (i.e., it does not matter) what the types of the HashMap are, for example:
public static void printHashMapSize(Map<?, ?> anyMap) {
// This code doesn't care what type of HashMap is inside anyMap.
System.out.println(anyMap.size());
}
Otherwise, specify the types that you need:
public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
for (int i = 'A'; i <= 'Z'; i++)
System.out.println(anyCharacterMap.get((char) i));
}
In the above method, we'd need to know that the Map's key is a Character, otherwise, we wouldn't know what type to use to get values from it. All objects have a toString() method, however, so the map can have any type of object for its values. We can still print the values.

Categories