Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java map with values limited by key's type parameter

Tags:

java

generics

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.

like image 691
Ashley Mercer Avatar asked Jan 06 '09 13:01

Ashley Mercer


People also ask

How HashMap stores different data types?

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.

Can map have multiple values for same key?

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.

Can a map have multiple values for same key Java?

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.

Can Hashmaps have multiple values?

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.


2 Answers

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.

like image 111
cletus Avatar answered Sep 21 '22 04:09

cletus


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();     }  } 
like image 40
Aaron Digulla Avatar answered Sep 24 '22 04:09

Aaron Digulla