107

How can I save a HashMap Object into Shared Preferences in Android?

Jacopo Sciampi
  • 2,990
  • 1
  • 20
  • 44
jibysthomas
  • 1,601
  • 2
  • 16
  • 24

12 Answers12

98

I use Gson to convert HashMap to String and then save it to SharedPrefs

private void hashmaptest()
{
    //create test hashmap
    HashMap<String, String> testHashMap = new HashMap<String, String>();
    testHashMap.put("key1", "value1");
    testHashMap.put("key2", "value2");

    //convert to string using gson
    Gson gson = new Gson();
    String hashMapString = gson.toJson(testHashMap);

    //save in shared prefs
    SharedPreferences prefs = getSharedPreferences("test", MODE_PRIVATE);
    prefs.edit().putString("hashString", hashMapString).apply();

    //get from shared prefs
    String storedHashMapString = prefs.getString("hashString", "oopsDintWork");
    java.lang.reflect.Type type = new TypeToken<HashMap<String, String>>(){}.getType();
    HashMap<String, String> testHashMap2 = gson.fromJson(storedHashMapString, type);

    //use values
    String toastString = testHashMap2.get("key1") + " | " + testHashMap2.get("key2");
    Toast.makeText(this, toastString, Toast.LENGTH_LONG).show();
}
penduDev
  • 4,743
  • 35
  • 37
  • 2
    how to get hashmap from gson i got error msg like com.qb.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 – – Ram Feb 26 '15 at 10:46
  • Expected BEGIN_OBJECT but was BEGIN_ARRAY is happening because HashMap should be HashMap, if the values are always String object you will not have any problems but if the value of some key is diff then String (for example custom object , or list or array) then exception will be thrown . So to be able to parse back everything you need HashMap – Stoycho Andreev Jul 13 '16 at 22:11
87

I would not recommend writing complex objects into SharedPreference. Instead I would use ObjectOutputStream to write it to the internal memory.

File file = new File(getDir("data", MODE_PRIVATE), "map");    
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file));
outputStream.writeObject(map);
outputStream.flush();
outputStream.close();
Kirill Rakhman
  • 42,195
  • 18
  • 124
  • 148
52
private void saveMap(Map<String,Boolean> inputMap) {
    SharedPreferences pSharedPref = getApplicationContext().getSharedPreferences("MyVariables", Context.MODE_PRIVATE);
    if (pSharedPref != null){
        JSONObject jsonObject = new JSONObject(inputMap);
        String jsonString = jsonObject.toString();
        pSharedPref.edit()
            .remove("My_map")
            .putString("My_map", jsonString)
            .apply();
    }
}

private Map<String,Boolean> loadMap() {
    Map<String,Boolean> outputMap = new HashMap<>();
    SharedPreferences pSharedPref = getApplicationContext().getSharedPreferences("MyVariables", Context.MODE_PRIVATE);
    try {
        if (pSharedPref != null) {
            String jsonString = pSharedPref.getString("My_map", (new JSONObject()).toString());
            if (jsonString != null) {
                JSONObject jsonObject = new JSONObject(jsonString);
                Iterator<String> keysItr = jsonObject.keys();
                while (keysItr.hasNext()) {
                    String key = keysItr.next();
                    Boolean value = jsonObject.getBoolean(key); 
                    outputMap.put(key, value);
                }
            }
        }
    } catch (JSONException e){
        e.printStackTrace();
    }
    return outputMap;
}
elcuco
  • 8,948
  • 9
  • 47
  • 69
Vinoj John Hosan
  • 6,448
  • 2
  • 41
  • 37
  • perfect answer :) – Ramkesh Yadav Jul 21 '18 at 12:10
  • How can I access `getApplicationContext` from a simple class? – Dmitry Aug 19 '18 at 01:49
  • @Dmitry A shortcut: In your simple class, include set context method and set the context as member variable and use it accordingly – Vinoj John Hosan Aug 24 '18 at 11:50
  • What's the point of the strangely complicated default value for `getString()`? Wouldn't a simple null or empty string work just as well? – SMBiggs Jun 29 '21 at 14:59
  • This code is bad: 1) you catch a generic exception instead of a json exception. Why do you need the generic? Since you may have NPE, which you can avoid by checking for null of the json string (you already do this for the shared preference). – elcuco Sep 02 '21 at 13:02
  • This code is bad: 2) for saving you commit twice instead of a single apply at the editor also chain the responces of the editor - into a single logical command. – elcuco Sep 02 '21 at 13:05
32
Map<String, String> aMap = new HashMap<String, String>();
aMap.put("key1", "val1");
aMap.put("key2", "val2");
aMap.put("Key3", "val3");

SharedPreferences keyValues = getContext().getSharedPreferences("Your_Shared_Prefs"), Context.MODE_PRIVATE);
SharedPreferences.Editor keyValuesEditor = keyValues.edit();

for (String s : aMap.keySet()) {
    keyValuesEditor.putString(s, aMap.get(s));
}

keyValuesEditor.commit();
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
hovanessyan
  • 30,580
  • 6
  • 55
  • 83
  • but i need to save the hash map as itself like we adding vector into the shared preferences – jibysthomas Oct 30 '11 at 13:33
  • than you have to probably use serialization and save the serialized HashMap in SharedPrefs. You can easily find code-examples on how to do that. – hovanessyan Oct 30 '11 at 13:49
12

As a spin off of Vinoj John Hosan's answer, I modified the answer to allow for more generic insertions, based on the key of the data, instead of a single key like "My_map".

In my implementation, MyApp is my Application override class, and MyApp.getInstance() acts to return the context.

public static final String USERDATA = "MyVariables";

private static void saveMap(String key, Map<String,String> inputMap){
    SharedPreferences pSharedPref = MyApp.getInstance().getSharedPreferences(USERDATA, Context.MODE_PRIVATE);
    if (pSharedPref != null){
        JSONObject jsonObject = new JSONObject(inputMap);
        String jsonString = jsonObject.toString();
        SharedPreferences.Editor editor = pSharedPref.edit();
        editor.remove(key).commit();
        editor.putString(key, jsonString);
        editor.commit();
    }
}

private static Map<String,String> loadMap(String key){
    Map<String,String> outputMap = new HashMap<String,String>();
    SharedPreferences pSharedPref = MyApp.getInstance().getSharedPreferences(USERDATA, Context.MODE_PRIVATE);
    try{
        if (pSharedPref != null){
            String jsonString = pSharedPref.getString(key, (new JSONObject()).toString());
            JSONObject jsonObject = new JSONObject(jsonString);
            Iterator<String> keysItr = jsonObject.keys();
            while(keysItr.hasNext()) {
                String k = keysItr.next();
                String v = (String) jsonObject.get(k);
                outputMap.put(k,v);
            }
        }
    }catch(Exception e){
        e.printStackTrace();
    }
    return outputMap;
}
Kyle Falconer
  • 8,302
  • 6
  • 48
  • 68
  • How can I access MyApp from a library? – Dmitry Aug 19 '18 at 01:52
  • @Dmitry You would do this in the same way you would access the `Context` instance from a library. Check out this other SO question: [Is it possible to get application's context in an Android Library Project?](https://stackoverflow.com/q/7157501/940217) – Kyle Falconer Aug 20 '18 at 18:41
9

map -> string

val jsonString: String  = Gson().toJson(map)
preferences.edit().putString("KEY_MAP_SAVE", jsonString).apply()

string -> map

val jsonString: String = preferences.getString("KEY_MAP_SAVE", JSONObject().toString())
val listType = object : TypeToken<Map<String, String>>() {}.type
return Gson().fromJson(jsonString, listType)
Evgen But
  • 159
  • 1
  • 3
2

You could try using JSON instead.

For saving

try {
    HashMap<Integer, String> hash = new HashMap<>();
    JSONArray arr = new JSONArray();
    for(Integer index : hash.keySet()) {
        JSONObject json = new JSONObject();
        json.put("id", index);
        json.put("name", hash.get(index));
        arr.put(json);
    }
    getSharedPreferences(INSERT_YOUR_PREF).edit().putString("savedData", arr.toString()).apply();
} catch (JSONException exception) {
    // Do something with exception
}

For getting

try {
    String data = getSharedPreferences(INSERT_YOUR_PREF).getString("savedData");
    HashMap<Integer, String> hash = new HashMap<>();
    JSONArray arr = new JSONArray(data);
    for(int i = 0; i < arr.length(); i++) {
        JSONObject json = arr.getJSONObject(i);
        hash.put(json.getInt("id"), json.getString("name"));
    }
} catch (Exception e) {
    e.printStackTrace();
}
Jonas Borggren
  • 2,591
  • 1
  • 22
  • 40
1

You can use this in a dedicated on shared prefs file (source: https://developer.android.com/reference/android/content/SharedPreferences.html):

getAll

added in API level 1 Map getAll () Retrieve all values from the preferences.

Note that you must not modify the collection returned by this method, or alter any of its contents. The consistency of your stored data is not guaranteed if you do.

Returns Map Returns a map containing a list of pairs key/value representing the preferences.

sivi
  • 10,654
  • 2
  • 52
  • 51
  • I think this is actually a better and simpler idea. You just create a dedicated SharedPreferences for this map and you are good to go besides an extra transformation from/to the actual data type. – Henry Aug 21 '21 at 17:03
1
String converted = new Gson().toJson(map);
SharedPreferences sharedPreferences = getSharedPreferences("sharepref",Context.MODE_PRIVATE);
sharedPreferences.edit().putString("yourkey",converted).commit();
1

The lazy way: storing each key directly in SharedPreferences

For the narrow use case when your map is only gonna have no more than a few dozen elements you can take advantage of the fact that SharedPreferences works pretty much like a map and simply store each entry under its own key:

Storing the map

Map<String, String> map = new HashMap<String, String>();
map.put("color", "red");
map.put("type", "fruit");
map.put("name", "Dinsdale");


SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
// OR use a specific pref name
// context.getSharedPreferences("myMegaMap");

for (Map.Entry<String, String> entry : map.entrySet()) {
    prefs.edit().putString(entry.getKey(), entry.getValue());
}

Reading keys from the map

SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
// OR use a specific pref name
// context.getSharedPreferences("myMegaMap");
prefs.getString("color", "pampa");

In case where you use a custom preference name (i.e. context.getSharedPreferences("myMegaMap")) you can also get all keys with prefs.getAll()

Your values can be of any type supported by SharedPreferences: String, int, long, float, boolean.

ccpizza
  • 28,968
  • 18
  • 162
  • 169
0

using File Stream

fun saveMap(inputMap: Map<Any, Any>, context: Context) {
    val fos: FileOutputStream = context.openFileOutput("map", Context.MODE_PRIVATE)
    val os = ObjectOutputStream(fos)
    os.writeObject(inputMap)
    os.close()
    fos.close()
}

fun loadMap(context: Context): MutableMap<Any, Any> {
    return try {
        val fos: FileInputStream = context.openFileInput("map")
        val os = ObjectInputStream(fos)
        val map: MutableMap<Any, Any> = os.readObject() as MutableMap<Any, Any>
        os.close()
        fos.close()
        map
    } catch (e: Exception) {
        mutableMapOf()
    }
}

fun deleteMap(context: Context): Boolean {
    val file: File = context.getFileStreamPath("map")
    return file.delete()
}

Usage Example:

var exampleMap: MutableMap<Any, Any> = mutableMapOf()
exampleMap["2"] = 1
saveMap(exampleMap, applicationContext) //save map

exampleMap = loadMap(applicationContext) //load map
Sos.
  • 914
  • 10
  • 14
0

You don't need to save HashMap to file as someone else suggested. It's very well easy to save a HashMap and to SharedPreference and load it from SharedPreference when needed. Here is how:
Assuming you have a

class T

and your hash map is:

HashMap<String, T>

which is saved after being converted to string like this:

       SharedPreferences sharedPref = getSharedPreferences(
            "MyPreference", Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPref.edit();
    editor.putString("MyHashMap", new Gson().toJson(mUsageStatsMap));
    editor.apply();

where mUsageStatsMap is defined as:

HashMap<String, T>
 

The following code will read the hash map from saved shared preference and correctly load back into mUsageStatsMap:

Gson gson = new Gson();
String json = sharedPref.getString("MyHashMap", "");
Type typeMyType = new TypeToken<HashMap<String, UsageStats>>(){}.getType();
HashMap<String, UsageStats> usageStatsMap = gson.fromJson(json, typeMyType);
mUsageStatsMap = usageStatsMap;  

The key is in Type typeMyType which is used in * gson.fromJson(json, typeMyType)* call. It made it possible to load the hash map instance correctly in Java.

us_david
  • 4,431
  • 35
  • 29