2

I am getting

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

at LinkedHashMap<String, String> dataHashMap = (LinkedHashMap<String, String>) intent.getSerializableExtra("tests.test.MyApp.A.EXTRA_KEY"); in the given (relevant parts of) code of Activity B.


  1. In Fragment A, I have the following check:

    Log.i(TAG, "THE TYPE OF THE MAP RETURNED FROM populateSelectedEntryList(selectedResultEntry) IS " + 
    populateSelectedEntryList(selectedResultEntry).getClass().getSimpleName()
    + ".");//******PRINTS LinkedHashMap**********************
    

which prints LinkedHashMap.

  1. In Activity B, I have the following check:

    LinkedHashMap<String, String> dataHashMap = (LinkedHashMap<String, String>)intent.getSerializableExtra("tests.test.MyApp.A.EXTRA_KEY");//**********ClassCastException*****************
    

    which prints HashMap.


The question is why? Why is the system converting my LinkedHashMap to HashMap. How do i force the LinkedHashMap to continue to be a LinkedHashMap when it is passed in an intent as a Serializable?

RELEVANT PARTS OF CODE:

In Fragment A,

public class A extends Fragment {
  ...

  public void onListItemClick(...) { 
    Intent intent = new Intent(getActivity(), B.class);
    ...
    intent.putExtra("tests.test.MyApp.A.EXTRA_KEY", populateSelectedEntryList(selectedResultEntry));
    Log.i(TAG, "THE TYPE OF THE MAP RETURNED FROM populateSelectedEntryList(selectedResultEntry) IS "
 + populateSelectedEntryList(selectedResultEntry).getClass().getSimpleName() + ".");//******PRINTS LinkedHashMap**********************
    startActivity(intent);
  };

  ...

  private LinkedHashMap<String, String> populateSelectedEntryList(MyParser.Entry selectedResultEntry) {
    LinkedHashMap<String, String> selectedEntryMap = new LinkedHashMap<>();
    ...
    return selectedEntryMap;
  }
}

And inside Activity B,

public class B extends FragmentActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
        ...
        Intent intent = getIntent();
        LinkedHashMap<String, String> dataHashMap = (LinkedHashMap<String, String>) intent.getSerializableExtra("tests.test.MyApp.A.EXTRA_KEY");
  } //**********ClassCastException*****************
  ...
}
Solace
  • 8,612
  • 22
  • 95
  • 183
  • You should reduce the question to the smallest amount of compilable code needed to reproduce the issue. There are lots of ... in your code which makes it hard to see what is going on. You haven't even posted the method that returns the HashMap (Intent.getSerializableExtra) – user1886323 Aug 01 '15 at 16:06
  • 1
    It's the same with `LinkedList` being converted to `ArrayList`: http://stackoverflow.com/questions/12300886/linkedlist-put-into-intent-extra-gets-recast-to-arraylist-when-retrieving-in-nex – Paul Boddington Aug 01 '15 at 16:11
  • @pbabcdefp Oh, that is the most terrible thing that has happened today. So there is no solution to this? I need this to work =( – Solace Aug 01 '15 at 16:29
  • @Solace You can store it as a pair of `Lists`, one for the keys and one for the values. If you try to store it as a `Map`, there is no way to stop it being turned into a `HashMap` at the other end, and the order of the keys will be lost. – Paul Boddington Aug 01 '15 at 16:34
  • 1
    Is the ordering implied by the LinkedHashMap important to your app? – schtever Aug 01 '15 at 16:43
  • @pbabcdefp That sounds like a lot of memory overhead, isn't it? Actually I need an _ordered, key-value pair data-structure_ which provides facility of `index-based search`? Can you suggest something? If there is no option, I'll have to use a pair of Lists, in a Bundle probably. – Solace Aug 01 '15 at 16:45
  • 1
    Well if it's a different order you want, you should be using a `TreeMap` anyway. You'll still have the same problem. I'm only talking about converting it into a pair of `List`s in order to put it in the `Intent` and recover it at the other end; you should not be *using* a pair of `List`s. In terms of memory overhead, a pair of `List`s is **less** memory intensive than a `TreeMap` or a `LinkedHashMap` because it doesn't require a load of `Map.Entry` objects. – Paul Boddington Aug 01 '15 at 16:56
  • @schtever Oh NO no no, the order of insertion IS important. Sorry I was mistaken. I am going to remove the above comment where I said it isn't important. – Solace Aug 01 '15 at 16:56
  • @pbabcdefp That's a very useful comment. Although it provides a solution which seems 'hacky' but is a solution to this problem nonetheless, and seems to be the only solution. If you can post it as an answer, it will be useful for future visitors. – Solace Aug 01 '15 at 17:01
  • @Solace I'll do that. Unfortunately, this is android, so you have to use horrible hacks like this all the time... – Paul Boddington Aug 01 '15 at 17:02
  • @pbabcdefp Thank you. Are there that kind of hacks in real applications, which a lot of people use? – Solace Aug 01 '15 at 17:03
  • 1
    I'm not really an expert on android. All I know is that I have to use hacks like this all the time, but I don't find myself doing horrible things like that when programming in pure Java or C#. – Paul Boddington Aug 01 '15 at 17:05

2 Answers2

5

The explanation for this is given here: LinkedList put into Intent extra gets recast to ArrayList when retrieving in next activity

If you want to recover a LinkedList, or a Map where the order of the keys is not important, you can use the solution of @schtever. If you want to recover a LinkedHashMap or a TreeMap and the order of the keys is important, it is a bit more tricky.

One possible solution is to convert the Map into a pair of Lists, as demonstrated below:

Activity1:

public class Activity1 extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Map<Integer, String> map = new LinkedHashMap<Integer, String>();
        map.put(1, "ONE");
        map.put(2, "TWO");
        map.put(3, "THREE");

        ArrayList<Integer> keys = new ArrayList<Integer>();
        ArrayList<String> values = new ArrayList<String>();
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            keys.add(entry.getKey());
            values.add(entry.getValue());
        }

        Intent intent = new Intent(this, Activity2.class);
        intent.putExtra("KEYS", keys);
        intent.putExtra("VALUES", values);

        startActivity(intent);
    }
}

Activity2:

public class Activity2 extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = getIntent();
        List<Integer> keys = (List<Integer>) intent.getSerializableExtra("KEYS");
        List<String> values = (List<String>) intent.getSerializableExtra("VALUES");

        Map<Integer, String> map = new LinkedHashMap<Integer, String>();
        for (int i = 0, n = keys.size(); i < n; i++)
            map.put(keys.get(i), values.get(i));

        Log.i("Activity2",  "" + map);
    }
}

This is a bit of a hack, and it's extremely verbose, but it works. I would be interested to know if there is a better solution to this.

Community
  • 1
  • 1
Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • 1
    This also helped me with similiar problem in onRestoreInstanceState / onSaveInstanceState thx – cinatic Aug 13 '16 at 12:44
  • In my case, fragment arguments were deserialized when the activity was recreated and the LinkedHashMap came out of the Bundle as HashMap. – dm78 Dec 01 '16 at 19:39
0

If ordering isn't important, recreate the LinkedHashMap using the returned HashMap:

LinkedHashMap<String, String> dataHashMap = new LinkedHashMap(intent.getSerializableExtra("tests.test.MyApp.A.EXTRA_KEY"));
schtever
  • 3,210
  • 16
  • 25