Is there a way in Java to have a map where the type parameter of a value is tied to the type parameter of a key? What I want to write is something like the following:
public class Foo { // This declaration won't compile - what should it be? private static Map<Class<T>, T> defaultValues; // These two methods are just fine public static <T> void setDefaultValue(Class<T> clazz, T value) { defaultValues.put(clazz, value); } public static <T> T getDefaultValue(Class<T> clazz) { return defaultValues.get(clazz); } }
That is, I can store any default value against a Class object, provided the value's type matches that of the Class object. I don't see why this shouldn't be allowed since I can ensure when setting/getting values that the types are correct.
EDIT: Thanks to cletus for his answer. I don't actually need the type parameters on the map itself since I can ensure consistency in the methods which get/set values, even if it means using some slightly ugly casts.
4.2. First, let's see how to declare the Map and put various types' data in it: Map<String, DynamicTypeValue> theMap = new HashMap<>(); theMap. put("E1 (Integer)", new IntegerTypeValue(intValue)); theMap. put("E2 (IntArray)", new IntArrayTypeValue(intArray)); theMap.
If you call put(K, V) twice, with the same key but different values, the multimap contains mappings from the key to both values. Show activity on this post. I use Map<KeyType, Object[]> for associating multiple values with a key in a Map. This way, I can store multiple values of different types associated with a key.
If this is an application requirement, the three best ways to solve the 'multiple values per key in a map in Java' problem are: Stick with the standard APIs and add a collection class like a 'Vector' or 'ArrayList' to your map or set. Use the MultiMap and MultiValueMap classes from the Apache Commons library.
HashMap can be used to store key-value pairs. But sometimes you may want to store multiple values for the same key. For example: For Key A, you want to store - Apple, Aeroplane.
You're not trying to implement Joshua Bloch's typesafe hetereogeneous container pattern are you? Basically:
public class Favorites { private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>(); public <T> void setFavorite(Class<T> klass, T thing) { favorites.put(klass, thing); } public <T> T getFavorite(Class<T> klass) { return klass.cast(favorites.get(klass)); } public static void main(String[] args) { Favorites f = new Favorites(); f.setFavorite(String.class, "Java"); f.setFavorite(Integer.class, 0xcafebabe); String s = f.getFavorite(String.class); int i = f.getFavorite(Integer.class); } }
From Effective Java (2nd edition) and this presentation.
The question and the answers made me come up with this solution: Type-safe object map. Here is the code. Test case:
import static org.junit.Assert.*; import java.util.ArrayList; import java.util.List; import org.junit.Test; public class TypedMapTest { private final static TypedMapKey<String> KEY1 = new TypedMapKey<String>( "key1" ); private final static TypedMapKey<List<String>> KEY2 = new TypedMapKey<List<String>>( "key2" ); @Test public void testGet() throws Exception { TypedMap map = new TypedMap(); map.set( KEY1, null ); assertNull( map.get( KEY1 ) ); String expected = "Hallo"; map.set( KEY1, expected ); String value = map.get( KEY1 ); assertEquals( expected, value ); map.set( KEY2, null ); assertNull( map.get( KEY2 ) ); List<String> list = new ArrayList<String> (); map.set( KEY2, list ); List<String> valueList = map.get( KEY2 ); assertEquals( list, valueList ); } }
This is the Key class. Note that the type T
is never used in this class! It's purely for the purpose of type casting when reading the value out of the map. The field key
only gives the key a name.
public class TypedMapKey<T> { private String key; public TypedMapKey( String key ) { this.key = key; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ( ( key == null ) ? 0 : key.hashCode() ); return result; } @Override public boolean equals( Object obj ) { if( this == obj ) { return true; } if( obj == null ) { return false; } if( getClass() != obj.getClass() ) { return false; } TypedMapKey<?> other = (TypedMapKey<?>) obj; if( key == null ) { if( other.key != null ) { return false; } } else if( !key.equals( other.key ) ) { return false; } return true; } @Override public String toString() { return key; } }
TypedMap.java:
import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; public class TypedMap implements Map<Object, Object> { private Map<Object, Object> delegate; public TypedMap( Map<Object, Object> delegate ) { this.delegate = delegate; } public TypedMap() { this.delegate = new HashMap<Object, Object>(); } @SuppressWarnings( "unchecked" ) public <T> T get( TypedMapKey<T> key ) { return (T) delegate.get( key ); } @SuppressWarnings( "unchecked" ) public <T> T remove( TypedMapKey<T> key ) { return (T) delegate.remove( key ); } public <T> void set( TypedMapKey<T> key, T value ) { delegate.put( key, value ); } // --- Only calls to delegates below public void clear() { delegate.clear(); } public boolean containsKey( Object key ) { return delegate.containsKey( key ); } public boolean containsValue( Object value ) { return delegate.containsValue( value ); } public Set<java.util.Map.Entry<Object, Object>> entrySet() { return delegate.entrySet(); } public boolean equals( Object o ) { return delegate.equals( o ); } public Object get( Object key ) { return delegate.get( key ); } public int hashCode() { return delegate.hashCode(); } public boolean isEmpty() { return delegate.isEmpty(); } public Set<Object> keySet() { return delegate.keySet(); } public Object put( Object key, Object value ) { return delegate.put( key, value ); } public void putAll( Map<? extends Object, ? extends Object> m ) { delegate.putAll( m ); } public Object remove( Object key ) { return delegate.remove( key ); } public int size() { return delegate.size(); } public Collection<Object> values() { return delegate.values(); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With