Convert Map<String,Object> to Map<String,String>

JavaSyntaxMapType Conversion

Java Problem Overview


How can I convert Map<String,Object> to Map<String,String> ?

This does not work:

Map<String,Object> map = new HashMap<String,Object>(); //Object is containing String
Map<String,String> newMap =new HashMap<String,String>(map);

Java Solutions


Solution 1 - Java

Now that we have Java 8/streams, we can add one more possible answer to the list:

Assuming that each of the values actually are String objects, the cast to String should be safe. Otherwise some other mechanism for mapping the Objects to Strings may be used.

Map<String,Object> map = new HashMap<>();
Map<String,String> newMap = map.entrySet().stream()
     .collect(Collectors.toMap(Map.Entry::getKey, e -> (String)e.getValue()));

Solution 2 - Java

If your Objects are containing of Strings only, then you can do it like this:

Map<String,Object> map = new HashMap<String,Object>(); //Object is containing String
Map<String,String> newMap =new HashMap<String,String>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
       if(entry.getValue() instanceof String){
    	    newMap.put(entry.getKey(), (String) entry.getValue());
          }
 }

If every Objects are not String then you can replace (String) entry.getValue() into entry.getValue().toString().

Solution 3 - Java

Generic types is a compile time abstraction. At runtime all maps will have the same type Map<Object, Object>. So if you are sure that values are strings, you can cheat on java compiler:

Map<String, Object> m1 = new HashMap<String, Object>();
Map<String, String> m2 = (Map) m1;

Copying keys and values from one collection to another is redundant. But this approach is still not good, because it violates generics type safety. May be you should reconsider your code to avoid such things.

Solution 4 - Java

There are two ways to do this. One is very simple but unsafe:

Map<String, Object> map = new HashMap<String, Object>();
Map<String, String> newMap = new HashMap<String, String>((Map)map);  // unchecked warning

The other way has no compiler warnings and ensures type safety at runtime, which is more robust. (After all, you can't guarantee the original map contains only String values, otherwise why wouldn't it be Map<String, String> in the first place?)

Map<String, Object> map = new HashMap<String, Object>();
Map<String, String> newMap = new HashMap<String, String>();
@SuppressWarnings("unchecked") Map<String, Object> intermediate =
    (Map)Collections.checkedMap(newMap, String.class, String.class);
intermediate.putAll(map);

Solution 5 - Java

Not possible.

This a little counter-intuitive.

> You're encountering the "Apple is-a fruit" but "Every Fruit is not an Apple"

Go for creating a new map and checking with instance of with String

Solution 6 - Java

As you are casting from Object to String I recommend you catch and report (in some way, here I just print a message, which is generally bad) the exception.

    Map<String,Object> map = new HashMap<String,Object>(); //Object is containing String
    Map<String,String> newMap =new HashMap<String,String>();

    for (Map.Entry<String, Object> entry : map.entrySet()) {
        try{
            newMap.put(entry.getKey(), (String) entry.getValue());
        }
        catch(ClassCastException e){
            System.out.println("ERROR: "+entry.getKey()+" -> "+entry.getValue()+
                               " not added, as "+entry.getValue()+" is not a String");
        }
    }

Solution 7 - Java

Use the Java 8 way of converting a Map<String, Object> to Map<String, String>. This solution handles null values.

Map<String, String> keysValuesStrings = keysValues.entrySet().stream()
    .filter(entry -> entry.getValue() != null)
    .collect(Collectors.toMap(Entry::getKey, entry -> entry.getValue().toString()));

Solution 8 - Java

The following will transform your existing entries.

TransformedMap.decorateTransform(params, keyTransformer, valueTransformer)

Where as

MapUtils.transformedMap(java.util.Map map, keyTransformer, valueTransformer)

only transforms new entries into your map

Solution 9 - Java

private Map<String, String> convertAttributes(final Map<String, Object> attributes) {
    final Map<String, String> result = new HashMap<String, String>();
    for (final Map.Entry<String, Object> entry : attributes.entrySet()) {
        result.put(entry.getKey(), String.valueOf(entry.getValue()));
    }
    return result;
}

Solution 10 - Java

Great solutions here, just one more option that taking into consideration handling of null values:

Map<String,Object> map = new HashMap<>();

Map<String,String> stringifiedMap = map.entrySet().stream()
             .filter(m -> m.getKey() != null && m.getValue() !=null)
             .collect(Collectors.toMap(Map.Entry::getKey, e -> (String)e.getValue()));

Solution 11 - Java

While you can do this with brute casting and suppressed warnings

Map<String,Object> map = new HashMap<String,Object>();
// Two casts in a row.  Note no "new"!
@SuppressWarnings("unchecked")
Map<String,String> newMap = (HashMap<String,String>)(Map)map;  

that's really missing the whole point. :)

An attempt to convert a narrow generic type to a broader generic type means you're using the wrong type in the first place.

As an analogy: Imagine you have a program that does volumous text processing. Imagine that you do first half of the processing using Objects (!!) and then decide to do the second half with correct-typing as a String, so you narrow-cast from Object to String. Fortunately, you can do this is java (easily in this case) - but it's just masking the fact you're using weak-typing in the first half. Bad practice, no argument.

No difference here (just harder to cast). You should always use strong typing. At minimum use some base type - then generics wildcards can be used ("? extends BaseType" or "? super BaseType") to give type-compatability and automatic casting. Even better, use the correct known type. Never use Object unless you have 100% generalised code that can really be used with any type.

Hope that helps! :) :)


Note: The generic strong typing and type-casting will only exist in .java code. After compilation to .class we are left with raw types (Map and HashMap) with no generic type parameters plus automatic type casting of keys and values. But it greatly helps because the .java code itself is strongly-typed and concise.

Solution 12 - Java

I find the easiest way by use guava:

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>30.1-jre</version>
    </dependency>

then you can do this:

Map<String,Object> map = new HashMap<String,Object>(); 
Map<String,String> newMap = Maps.transformValues(map, Functions.toStringFunction());

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMawiaView Question on Stackoverflow
Solution 1 - JavaskerylView Answer on Stackoverflow
Solution 2 - JavaShreyos AdikariView Answer on Stackoverflow
Solution 3 - JavaMikhailView Answer on Stackoverflow
Solution 4 - JavacambeccView Answer on Stackoverflow
Solution 5 - JavaSuresh AttaView Answer on Stackoverflow
Solution 6 - JavaseligView Answer on Stackoverflow
Solution 7 - JavaBJ Dela CruzView Answer on Stackoverflow
Solution 8 - JavayunspaceView Answer on Stackoverflow
Solution 9 - JavaSky TronicsView Answer on Stackoverflow
Solution 10 - JavaJohnnyView Answer on Stackoverflow
Solution 11 - JavaGlen BestView Answer on Stackoverflow
Solution 12 - JavajiangkeView Answer on Stackoverflow