简体   繁体   中英

How to change HashMap<K, V> value type from Object to String?

What is the easiest/best way to convert

Map<String, Object>

to

HashMap<String, String>

The API I am using has methods that return a Map but it would be easier if I didn't have to cast the Object to a String each time.

Also, is this even worth doing? Would a HashMap be faster/more efficient than a Map?

I'm assuming I'll have to loop through the original Map and copy the values to the new HashMap.

Thanks in advance!

You can use the constructor as others mentioned:

Map<String, String> newMap = new HashMap(oldMap);

This will only work however if you know that the Object s in question are really String s.

but there is something I should mention:

Do not confuse interfaces with classes . Map is just an interface ; a contract which contains only definitions. A class on the other hand is a concrete implementation of an interface . So it does not make any difference in terms of perfomrance if you use the Map interface or its runtime type ( HashMap ). It can make a difference however if you swap the implementations (to TreeMap for example).

Edit:

Here is the verbose solution which is liked by EE guys (no casting/rawtypes warning involved):

public class MapConverter {
    public Map<String, String> convert(Map<String, Object> oldMap) {
        Map<String, String> ret = new HashMap<String, String>();
        for (String key : oldMap.keySet()) {
            ret.put(key, oldMap.get(key).toString());
        }
        return ret;
    }
}

Using the copy constructor on raw types works:

HashMap<String, String> hashMap = new HashMap(map);

However, the solution is ugly as the type system is ignored.

EDIT1:

When you execute

public static void main(String[] args) throws IllegalArgumentException,
        InterruptedException, IOException {
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("Bla", new Object());
    HashMap<String, String> hashMap = new HashMap(map);
    System.out.println(hashMap.get("Bla").getClass());
}

you get the class cast exception:

Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String

It is thrown when "System.out.println(hashMap.get("Bla").getClass());" is executed.

Consequently, the casts are actually delayed.

EDIT2:

You can avoid the copy with

HashMap<String, String> hashMap = (HashMap)map;

However, the problem remains the same as the following code shows:

public static void main(String[] args) throws IllegalArgumentException,
        InterruptedException, IOException {
    HashMap<String, Object> oldMap = new HashMap<String, Object>();
    oldMap.put("Bla", new Object());
    HashMap<String, String> hashMap = (HashMap)oldMap;
    System.out.println(hashMap.get("Bla").getClass());
}

It behaves like the other example above in EDIT1.

EDIT3: What about using a lambda?

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

    // 1

    final Stream<Map.Entry<String, Object>> entries = map.entrySet()
            .stream();

    final Function<Map.Entry<String, Object>, String> keyMapper = (
            Map.Entry<String, Object> entry) -> entry.getKey();

    final Function<Map.Entry<String, Object>, String> valueMapper = (
            Map.Entry<String, Object> entry) -> {
        final Object value = entry.getValue();
        if (value instanceof String) {
            return (String) value;
        } else {
            throw new ClassCastException("Value '" + value + "' of key '"
                    + entry.getKey() + "' cannot be cast from type "
                    + ((value != null) ? value.getClass().getName() : null)
                    + " to type " + String.class.getName());
        }
    };

    final BinaryOperator<String> duplicateHandler = (key1, key2) -> {
        throw new IllegalStateException(String.format("Duplicate key %s",
                key1));
    };

    final HashMap<String, String> hashMap = entries.collect(Collectors
            .toMap(keyMapper, valueMapper, duplicateHandler, HashMap::new));

    System.out.println(hashMap);

If map only has string-to-string entries, it will copy them all. Eg Insert

    map.put("aKey", "aValue");

at comment 1. It will print

    {aKey=aValue}

which is fine.

If you have at least one string-to-non-string entry in your map, copying will fail.

Eg Insert

    map.put("aKey", 42);

at comment 1. It will print

Exception in thread "main" java.lang.ClassCastException: Value '42' of key ' aKey' cannot be cast from type java.lang.Integer to type java.lang.String
    at ...

which shows the string-to-non-string entry.

I know this solution is not so simple but it is safe.

If you know the types of key and value (like <String, String> ), you can just cast the whole map:

Map<String, String> newMap = (HashMap<String, String>)oldMap;

If you need a separate Map instance, you can use the constructor of HashMap like this:

HashMap<String, String> = new HashMap<String, String>((HashMap<String, String>) oldMap);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM