// I know that this method will generated duplicate
// trim keys for the same value but I am just
// trying to understand why we have a compile error:
// The method put(String, capture#11-of ?) in the type
// Map<String,capture#11-of ?> is not applicable for the arguments
// (String, capture#12-of ?)
void trimKeyMap(Map<String, ?> map){
for (String key : map.keySet()) {
map.put(StringUtils.trim(key), map.get(key)); // compile error
}
}
How come the value that we want to put map.get(key) can be from a different type?
The problem is that the compiler only knows the key type is "unknown", but doesn't know it's the same unknown type for the type of the Map's key and the type returned from get() (even though we as humans realize that it's the same).
If you want to make it work, you must tell the compiler it's the same unknown type by typing your method, for example:
void <V> trimKeyMap(Map<String, V> map) {
for (String key : map.keySet()) {
map.put(StringUtils.trim(key), map.get(key));
}
}
It might have been possible for the Java 5 expert group, to add a little more power to the specification of wildcards in generic method argument types. I suspect the enhancement didn't make it due to lack of time in the specs phase. See my own recent question here: How does the JLS specify that wildcards cannot be formally used within methods?
Short of a better solution, you have to make your method a generic method:
<V> void trimKeyMap(Map<String, V> map){
for (String key : map.keySet()) {
map.put(StringUtils.trim(key), map.get(key));
}
}
You cannot put anything in a Map<String, ?> type, except for the null literal:
Map<String, ?> map = new HashMap<String, String>();
map.put("A", null); // Works
map.put("B", "X"); // Doesn't work.
The compiler doesn't know that the map's value argument type was String. So it cannot allow you to add anything to the map, even if you got the value from the map itself.
You can use type inference in a helper method, if you want to keep the clean method signature (the client of trimKeyMap shouldn't have to use a generic method):
void trimKeyMap(final Map<String, ?> map) {
for (final String key : map.keySet()) {
trim(map, key);
}
}
private <T> void trim(final Map<String, T> map, final String key) {
map.put(StringUtils.trim(key), map.get(key));
}
This is called wildcard capture and is discussed more here: http://www.ibm.com/developerworks/library/j-jtp04298/
? is not a wildcard for any type but the unknown type. So the map only accepts ?-type objects and not String. One conclusion from this (rather strange) fact: We can't add values to collections that are parametized with ?.
? is different from Object.
A Map<String, ?> is a generic map where you don't know the element type, but it is still being enforced, i.e. it does not allow you to put anything.
You cannot add values (that are not null) to Collections and Maps parametrized with a wildcard ?. That is because a wildcard is not a substitute for any object, it's just an unknown object. So you cannot add any (lets put it that way) known object in a map that accepts only unknown.
You can only read values.
Related
I have this question regarding generics.Can anybody explain me why the options 4 and 6[i know about this option]are correct?
Consider the following code:
import java.util.*;
public class TestClass
{
public static void main(String[] args)
{
// put declaration here
m.put("1", new ArrayList()); //1
m.put(1, new Object()); //2
m.put(1.0, "Hello"); //3
System.out.println(m);
}
}
How can 'm' be declared such that the above code will compile and run without errors?
Map m = new TreeMap();
Map<Object, Object> m = new TreeMap<Object, Object>();
Map<Object, ?> map = new LinkedHashMap<Object, Object>();
Map<Object, ? super ArrayList> m = new LinkedHashMap<Object, ArrayList>();will work
if lines //2 and //3 are commented out.
Map<Object, ? super ArrayList> m = new LinkedHashMap<Object, ArrayList>(); will work if lines //1 and //3 are commented out.
Map m = new HashMap();
For understanding this problem, look at the generic signature of the Map#put method you are using here. It states:
V put(K key, V value)
what means that you can put a key that is assignable to the generic type of the Map's key type K and a value that is assignable to the generic type of the Map's value type V. This must be true for all your key-value pairs you put into the map. From your code, you are putting the following keys into the map:
A String by the literal "1"
An Integer by the boxed int literal 1.
A Double by the boxed double literal 1.0.
The only common super type of these objects is the Object type which is required for K in order to allow all these objects to be used as a key.
For the values you have:
A ArrayList instance.
An Object instance
A String by the literal "Hello"
Again, the only common super type of these objects is the Object type which is required for V in order to allow all these objects to be used as a map value.
As a result, only Map instances with the generic signature Map<Object, Object> are permitted. What implementation of the Map interface you choose is up to you, as long as it is assignable to the variable type of the map, i.e. you can use a LinkedHashMap, a TreeMap or a HashMap since they only differ in the way they store their data. For the generic type of the variables, note that the use of wildcards ? or ? extends ... for your variable will result in you not being able to put values into the map anymore. The tutorial I linked explains why.
As for the types with a non-generic signature you are using, they behave similar to Maps with the generic signature <Object, Object>. Such raw types should however not longer be used after the introduction of Java 5.
With all this information, you can answer your (exam) question yourself.
Number 4 is correct for line 1, because "1" is String which has Object superclass and ? super ArrayList means that you can use ArrayList or any superclass of ArrayList.
Number 6 is correct because you are using untyped(raw) map, so it's similar to:
Map<Object, Object> m = new HashMap<Object, Object>();
To store such values you can use Map, but it's not a really good choice. You shouldn't use untyped collections at all. Think how you can change your design to not use such map.
I am sick of the following pattern:
value = map.get(key);
if (value == null) {
value = new Object();
map.put(key, value);
}
This example only scratches the surface of the extra code to be written when you have nested maps to represent a multi-dimensional structure.
I'm sure something somewhere exists to avoid this, but my Googling efforts yielded nothing relevant. Any suggestions?
The
java.util.concurrent.ConcurrentMap
and from Java 8
Java.util.Map
has
putIfAbsent(K key, V value)
which returns the existing value, and if that is null inserts given value. So if no value exists for key returns null and inserts the given value, otherwise returns existing value
If you need lazy evaluation of the value there is
computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
Java 8 adds nice method to the Map: compute, computeIfPresent, computeIfAbsent
To achieve what you need:
Object existingOrCreated = map.computeIfAbsent(key, (k) -> new Object());
The problem with this pattern is that you'd have to somehow define the value that should be used in case the get() returns null.
There certainly are libraries out there and IIRC there are also some newer collections that do that, but unfortunately I don't remember which those were.
However, you could write a utility method yourself, provided you have a standard way of creating the new values. Something like this might work:
public static <K, V> V safeGet(K key, Map<K,V> map, Class<V> valueClass) throws /*add exceptions*/ {
V value = map.get(key);
if( value == null ) {
value = valueClass.newInstance();
map.put( key, value );
}
return value;
}
Note that you'd either have to throw the reflection exceptions or handle them in the method. Additionally, this requires the valueClass to provide a no-argument constructor. Alternatively, you could simply pass the default value that should be used.
Java 8 update
It has already been mentioned in other answers but for the sake of completeness I'll add the information here as well.
As of Java 8 there is the default method computeIfAbsent(key, mappingFunction) which basically does the same, e.g. if the value class was BigDecimal it could look like this:
BigDecimal value = map.computeIfAbsent(key, k -> new BigDecimal("123.456"));
The implementation of that method is similar to the safeGet(...) defined above but more flexible, directly available at the map instance and better tested. So when possible I'd recommend using computeIfAbsent() instead.
You can use MutableMap and getIfAbsentPut() from Eclipse Collections which returns the value mapped to the key or inserts the given value and returns the given value if no value is mapped to the key.
You can either use a method reference to create a new Object:
MutableMap<String, Object> map = Maps.mutable.empty();
Object value = map.getIfAbsentPut("key", Object::new);
Or you can directly create a new Object:
MutableMap<String, Object> map = Maps.mutable.empty();
Object value = map.getIfAbsentPut("key", new Object());
In the first example, the object will be created only if there is no value mapped to the key.
In the second example, the object will be created regardless.
Note: I am a contributor to Eclipse Collections.
If in any case you need to get a default data in your map if it's not existing
map.getOrDefault(key, defaultValue);
javadocs
EDIT : Note that the feature mentioned below is long deprecated, and a CacheBuilder should be used instead.
The Guava library has a "computing map", see MapMaker.makeComputingMap(Function).
Map<String, Object> map = new MapMaker().makeComputingMap(
new Function<String, Object>() {
public String apply(Stringt) {
return new Object();
}
});
If you need the Function several times, extract it into a utility class, and then create the Map like this (where MyFunctions.NEW_OBJECT is the static Function instance):
Map<String, Object> map = new MapMaker()
.makeComputingMap(MyFunctions.NEW_OBJECT);
Maybe I'm not seeing the whole problem, but how about using inheritance or composition to add this behavior to the Map object?
I have one method, check which has two hashmaps as parameters. Keys of these maps is a String and value is String or Arraylist.
Which is the better solution:
public static boolean check(HashMap<String, ?> map1, HashMap<String, ?> map2) {
for ( entry <String, ? > entry : map1.entryset()) {
...
}
}
or
public static <V> boolean check(HashMap<String, V> map1, HashMap<String, V> map2) {
for ( entry <String, V > entry : map1.entryset()) {
...
}
}
and why?
And can you also give me some more information about the difference between these two solutions?
In the first, the ? coul dbe anything. One could be <String, String> the other could be <String, Double>. In the second option they must be the same.
Now the first is acceptable as long as you have the ability to convert them so they're comparable. For example, you could do .toString() on both values to compare. But personally, I would prefer the second as it allows me to have more control over what's going on, and gives me compile time checking of types.
The second one enforces at compile-time that the two maps are parameterised the same as each other. It also allows you do something useful with the maps, such as inserting non-null elements into them (this isn't possible with wildcards).
I ran into a bug in my code where I was using the wrong key to fetch something from a Java map that I believed was strongly typed using Java generics. When looking at the Map Javadocs, many of the methods, including get and remove, take an Object as the parameter instead of type K (for a Map defined as Map). Why is this? Is there a good reason or is it an API design flaw?
I think this is for backwards compatibility with older versions of the Map interface. It's unfortunate that this is the case however as you're right, it would be much better if this took the correct type.
Because the map will return a value if the object passed to the get method is equal to any key stored in the map. Equal does not mean that they have to be of the same type, but that the key's and the passed object's equal methods are implemented in such a way, that the different object types are mutually recognized as equal.
The same applies of course to the remove method.
Example of valid code, which would break (not compile) if the get method only allowed parameters of type K:
LinkedList<Number> k1 = new LinkedList<Number>();
k1.add(10);
ArrayList<Integer> k2 = new ArrayList<Integer>();
k2.add(10);
Map<LinkedList<Number>, String> map = new HashMap<LinkedList<Number>, String>();
map.put(k1, "foo");
System.out.println(map.get(k2));
This was done so that if the type parameter is a wildcard, these methods can still be called.
If you have a Map<?, ?>, Java won't allow you to call any methods that are declared with the generic types as arguments. This prevents you from violating the type constraints so you cannot, for instance, call put(key, value) with the wrong types.
If get() were defined as get(K key) instead of the current get(Object key), it too would have been excluded due to this same rule. This would make a wildcarded Map practically unusable.
In theory, the same applies to remove(), as removing an object can never violate the type constraints either.
Here is an example of code that would not have been possible if get had been declared as get(T key):
public static <K,V> Map<K, V> intersect(Map<? extends K, ? extends V> m1, Map<? extends K, ? extends V> m2) {
Map<K,V> result = new HashMap<K, V>();
for (Map.Entry<? extends K, ? extends V> e1 : m1.entrySet()) {
V value = m2.get(e1.getKey()); // this would not work in case of Map.get(K key)
if (e1.getValue().equals(value)) {
result.put(e1.getKey(), e1.getValue());
}
}
return result;
}
e1.getKey() returns an object of some unknown subtype of K (the subtype used by m1), but m2 uses a potentially different subtype of K. Had Map.get() been declared as get(K key), this usage would not have been allowed.
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.