Let us say we have following enums:
public enum AnimalImages {
TIGER,BEAR,WOLF;
}
public enum CarImages {
BMW,VW,AUDI;
}
Now I want to save these enum classes in a HashMap:
Map<String,Enum<?>> enumMap=new HashMap<String,Enum<?>>();
enumMap.put("AnimalImages",???????);
What I should enter instead of the question marks?
To explicitly answer to your question, you have to put the enum value as this:
Map<String,Enum<?>> enumMap=new HashMap<String,Enum<?>>();
enumMap.put("AnimalImages", AnimalImages.TIGER);
However, if you want to put all the value belonging to an enum, then you could leverage values() method and also change your map to Map<String,Enum<?>[]> so you can use this:
Map<String,Enum<?>[]> enumMap=new HashMap<String,Enum<?>[]>(); // Note Enum<?>[] array
enumMap.put("AnimalImages", AnimalImages.values());
enumMap.put("CarImages", CarImages.values());
Another approach, with a shorten signature can be something like this:
Map<String, Object> enumMap = new HashMap<String,Object>();
enumMap.put("AnimalImages", AnimalImages.values());
enumMap.put("CarImages", CarImages.values());
Another way that bali182 pointed in this comment, you could use:
Map<String, Collection<? extends Enum<?>>> enumMap = new HashMap<>();
enumMap.put("AnimalImages", Arrays.asList(AnimalImages.values()));
enumMap.put("CarImages", Arrays.asList(AnimalImages.values()));
To answer your question:
enumMap.put("AnimalImages", AnimalImages.WOLF);
enumMap.put("Cars", CarImages.AUDI);
But you can also do
Map<String, AnimalImages> enumMap = new HashMap<String, AnimalImages>();
And this way enumMap.get("AnimalImages"), and this way you won't have to type check and cast it.
If the requirement is to create a Map that contains all values of a given enum, I would use a mapping of String keys into EnumSet values:
Map<String, EnumSet<?>> enumMap = new HashMap<String, EnumSet<?>>();
enumMap.put("AnimalImages", EnumSet.allOf(AnimalImages.class));
enumMap.put("CarImages", EnumSet.allOf(CarImages.class));
for (Map.Entry<String, EnumSet<?>> e : enumMap.entrySet()) {
System.out.println(String.format("key: [%s], values: %s",
e.getKey(), e.getValue().toString()));
}
EnumSet is a specialized Set implementation, which is represented internally as a bit vector. Its sole purpose is to provide an efficient and easy way to store enums.
Related
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.
Usually, if I know beforehand all the keys of a map, I instantiate it like this:
List<String> someKeyList = getSomeList();
Map<String, Object> someMap = new HashMap<String, Object>(someKeyList.size());
for (String key : someKeyList) {
someMap.put(key, null);
}
Is there any way to do this directly without needing to iterate through the list? Something to the effect of:
new HashMap<String, Object>(someKeyList)
My first thought was to edit the map's keyset directly, but the operation is not supported. Is there other way I'm overlooking?
You can use Java 8 Streams :
Map<String,Object> someMap =
someKeyList.stream()
.collect(Collectors.toMap(k->k,k->null));
Note that if you want a specific Map implementation, you'll have to use a different toMap method, in which you can specify it.
I want to create a typesafe collection which can store multiple collections of the same type but with typesafe parameters. The standart way:
Map<Key<?>, Object> container = new HashMap<>();
The key contains the type of the object and the get method casts to the correct type (standart typesafe hetereogeneous container pattern). But i need something like this:
container.put(Key, new HashMap<Long, String>);
The type itself would be safe (Map.class) but i don't know how to ensure that key and value of the map are of the type long and string. How can i do that with java?
EDIT
To make things clearer:
Map<Class<?>, Object> container = new HashMap<>();
Now the typesafe implementation of this map:
public <T> void put(Class<T> key, T value) {
container.put(key, value);
}
public <T> T get(Class<T> key) {
return key.cast(container.get(key));
}
I can to this in a typesafe way now:
containerClass.put(Double.class, 2.0);
containerClass.put(Integer.class, 3);
And of course:
containerClass.put(MyObject.class, myObject);
If i want to store multiple values of the same type than i could use a generic list instead of the object as a value or a specific key class which has an identifier as a field.
BUT
What happens now?
Map<String, Integer> map = new HashMap<>();
containerClass.put(Map.class, map);
With this implementation it is not safe that it is a map with String as a key and integer as the value. I want to store all kinds of objects and collections but the collections itself must be typesafe too.
Not sure what your Key class is but the following is perhaps what you are looking for, just replace String with your Key class
Map<String, Map<? extends Long, ? extends String>> container = new HashMap<>();
container.put("", new HashMap<Long, String>());
container.put("", new HashMap<Long, Long>());
if you try to compile that code, you will find the first put is okay but the 2nd will not compile due to non compliance to the Map type. That type states only an object that extends Long is accepted as the 1st parameter and the 2nd only accepts objects that extend String.
My string looks like;
String values = "I am from UK, and you are from FR";
and my hashtable;
Hashtable countries = new Hashtable();
countries.put("United Kingdom", new String("UK"));
countries.put("France", new String("FR"));
What would be the most effective way to change the values in my string with the values from the hashtable accordingly. These are just 2 values to change, but in my case I will have 100+
I'm not sure there's a whole lot you can do to optimize this in a way which is worthwhile. Actually you can construct an FSM for custom replacements like that but it's probably more than you want to really do.
Map<String, String> countries = new HashMap<String, String>();
countries.put("United Kingdom", "UK");
countries.put("France", "FR");
for (Map.Entry<String, String> entry : countries.entrySet()) {
values.replace(entry.getKey(), entry.getValue());
}
A couple of notes:
Don't use Hashtable. Use a Map (interface) and HashMap (class) instead;
Declare your variable, parameter and return types, where applicable, as interfaces not concrete classes;
Assuming you're using Java 5, use generic type arguments for more readable code. In this case, Map<String, String>, etc; and
Don't use new String("UK"). There is no need.
Several thoughts. First of all: why use hashtable? hashmap is usually faster as hashtable is synchronized.
Then: why not use generics?
HashMap<String, String>
is much more expressive than just HashMap
Third: don't use new String("UK"), "UK" will do fine, you're creating the same string twice.
But to solve your problem, you probably want to turn the map around:
Map<String,String> countries = new HashMap<String, String>();
countries.put("UK", "United Kingdom");
countries.put("FR", "France");
Now if I understand you right you want to do something like this:
String values = "I am from UK, and you are from FR";
for(Map.Entry<String, String> entry : countries.entrySet()){
values = values.replace(entry.getKey(), entry.getValue());
}
Is there any shorthand way of defining and using generic definitions without having to keep repeating a particular generic description such that if there is a change I don't have to change all definitions/usages though out the codebase for example is something like this possible:
Typedef myGenDef = < Object1, Object2 >;
HashMap< myGenDef > hm = new HashMap< myGenDef >();
for (Entry< myGenDef > ent : hm..entrySet())
{
.
.
.
}
There's the pseudo-typedef antipattern...
class StringList extends ArrayList<String> { }
Good stuff, drink up! ;-)
As the article notes, this technique has some serious issues, primarily that this "typedef" is actually a separate class and thus cannot be used interchangeably with either the type it extends or other similarly defined types.
In a generic method, you can use a limited form of type inferrence to avoid some repetitions.
Example: if you have the function
<K, V> Map<K, V> getSomething() {
//...
}
you can use:
final Map<String, Object> something = getsomething();
instead of:
final Map<String, Object> something = this.<String, Object>getsomething();
Use Factory Pattern for creation of Generics:
Method Sample:
public Map<String, Integer> createGenMap(){
return new HashMap<String,Integer>();
}
The pseudo-typedef antipattern mentioned by Shog9 would work - though it's not recommended to use an ANTIPATTERN - but it does not address your intentions. The goal of pseudo-typedef is to reduce clutter in declaration and improve readability.
What you want is to be able to replace a group of generics declarations by one single trade. I think you have to stop and think: "in witch ways is it valuable?". I mean, I can't think of a scenario where you would need this. Imagine class A:
class A {
private Map<String, Integer> values = new HashMap<String, Integer>();
}
Imagine now that I want to change the 'values' field to a Map. Why would exist many other fields scattered through the code that needs the same change? As for the operations that uses 'values' a simple refactoring would be enough.
No. Though, groovy, a JVM language, is dynamically typed and would let you write:
def map = new HashMap<complicated generic expression>();