Android LinkedHashMap deserializing as HashMap, causes CCE error

妖精的绣舞 提交于 2019-12-22 12:27:14

问题


I'm attempting to pass a serialized LinkedHashMap between activities, and getting a confusing result/error when I deserialize the object.

I serialize the object as follows:

    Bundle exDetails = new Bundle();
    LinkedHashMap<String, Exercise> exMap = new LinkedHashMap<String, 
    Exercise (workout.getExercises());

    exDetails.putString(WORKOUTNAME, workout.getWorkoutName());
    exDetails.putSerializable(workout.getWorkoutName(), exMap);

    iComplete.putExtra("wName", exDetails);
    startActivity(iComplete);

That seems to work fine, the problem shows up in the next activity:

    Bundle exDetails =  getIntent().getBundleExtra("wName");
    workoutName = exDetails.getString(WORKOUTNAME);
    Serializable eData = exDetails.getSerializable(workoutName);

    ex = new LinkedHashMap<String, Exercise>();
    ex = (LinkedHashMap<String, Exercise>) eData;

At this point, the deserialized object (eData) contains a HashMap object (not LinkedHashMap), and it gives me

java.lang.ClassCastException:java.util.HashMap cannot be
cast to java.util.LinkedHashMap

on that last line. I've verified with the debugger that the bundle (in the second activity) contains a HashMap, instead of a LinkedHashMap (as I'm assuming it should). I should also mention that I need to maintain the order in which entries are added to the Map, hence the usage of LinkedHashMap. The entries eventually get printed, and order is very important for the output.

Questions: Am I doing anything wrong in particular, or is this problem due to bugs with LinkedHashMap's serialization? I've noticed a few similar threads, that seem to speak of this being an ongoing problem with several of the Map implementations. They didn't answer my problem directly though.

If the latter, is there a workaround that isn't too advanced (I'm not far beyond beginner level, but I'm willing to try most things), or do I need to just bite the bullet and work something other than LinkedHashMap?

P.s. I tried to include everything relevant, but I can add more code if I left out anything important.


回答1:


I went for a different approach: serialize any (type of) Map into 2 ArrayLists: one containing the keys and the other one containing the values. This way, the order of the map entries (important in a LinkedHashMap) is kept.

Each key/value implements Serializable. If you know for sure you need just Strings or just one particular type of Map, then it should be really easy to convert the following generic code into the scenario needed which also simplifies the complexity.

Map -> 2 ArrayLists:

public static <K extends Serializable, V extends Serializable> Pair<ArrayList<K>, ArrayList<V>> convertMapToArrays(@NonNull Map<K, V> map) {
        final Set<Map.Entry<K, V>> entries = map.entrySet();
        final int size = entries.size();
        final ArrayList<K> keys = new ArrayList<>(size);
        final ArrayList<V> values = new ArrayList<>(size);
        for (Map.Entry<K, V> entry : entries) {
            keys.add(entry.getKey());
            values.add(entry.getValue());
        }
        return new Pair<>(keys, values);
}

2 ArrayLists -> Map of a specific type

 public static <K extends Serializable, V extends Serializable> Map<K, V> convertArraysToMap(@NonNull ArrayList<K> keys, @NonNull ArrayList<V> values, @NonNull Class<? extends Map<K, V>> mapClass) {
        if (keys.size() != values.size()) {
            throw new RuntimeException("keys and values must have the same number of elements");
        }
        final int size = keys.size();
        Map<K, V> map;
        try {
            final Constructor<? extends Map<K, V>> constructor = mapClass.getConstructor(Integer.TYPE);
            map = constructor.newInstance(size);
        } catch (Exception nse) {
            throw new RuntimeException("Map constructor that accepts the initial capacity not found.");
        }

        for (int i = 0; i < size; i++) {
            final K key = keys.get(i);
            final V value = values.get(i);
            map.put(key, value);
        }
        return map;
    }

Helpers for Android's Bundle:

 public static <K extends Serializable, V extends Serializable> void saveMapToBundleAsArrays(@NonNull Map<K, V> map, @NonNull String key, @NonNull Bundle bundle) {
        final Pair<ArrayList<K>, ArrayList<V>> mapToArrays = convertMapToArrays(map);
        final String keyForKeys = key + "_keys";
        final String keyForValues = key + "_values";
        bundle.putSerializable(keyForKeys, mapToArrays.first);
        bundle.putSerializable(keyForValues, mapToArrays.second);
    }

public static Map<Serializable, Serializable> loadMapFromBundle(@NonNull Bundle bundle, @NonNull String key, @NonNull Class<? extends Map<Serializable, Serializable>> mapClass) {
        final String keyForKeys = key + "_keys";
        final String keyForValues = key + "_values";
        final ArrayList<Serializable> keys = (ArrayList<Serializable>) bundle.getSerializable(keyForKeys);
        final ArrayList<Serializable> values = (ArrayList<Serializable>) bundle.getSerializable(keyForValues);
        return convertArraysToMap(keys, values, mapClass);
    }

Usage:

saveMapToBundleAsArrays(mModelEvolution, KEY_MODEL_DATA, bundle);

Class<LinkedHashMap<Serializable, Serializable>> linkedHashMapClazz =
                (Class<LinkedHashMap<Serializable, Serializable>>) new LinkedHashMap<String, String>().getClass();
mModelEvolution = (LinkedHashMap) ObjectUtils.loadMapFromBundle(bundle, KEY_MODEL_DATA, linkedHashMapClazz);


来源:https://stackoverflow.com/questions/25771416/android-linkedhashmap-deserializing-as-hashmap-causes-cce-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!