Storing a Map between Runtimes - java

I have been reading some posts on how to store data between runtimes, between HBase, Serialization, and other stuff, but is there a way to store a Map(Object, Set of difObject) easily? I have been watching videos and reading posts and I just havent been able to wrap my brain around it, also wherever I store the data cannot be human readable as it has personal information on it.

Use java.io.ObjectOutputStream and java.io.ObjectInputStream to persist Java objects (in your case: write/read the Map). Make sure that all objects you're persisting implement Serializable.
Example: writing data(marshalling)
Map<String, Set<Integer>> map = new HashMap<String, Set<Integer>>();
map.put("Foo", new HashSet<Integer>(Arrays.asList(1, 2, 3)));
map.put("Bla", new HashSet<Integer>(Arrays.asList(4, 5, 6)));
File file = new File("data.bin");
ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
try {
out.writeObject(map);
out.flush();
} finally {
out.close();
}
Reading the stored data (unmarshalling)
File file = new File("data.bin");
if (file.exists()) {
ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)));
try {
Map<String, Set<Integer>> read = (Map<String, Set<Integer>>) in.readObject();
for (String key : read.keySet()) {
System.out.print(key + ": ");
Set<Integer> values = read.get(key);
for (Integer value : values) {
System.out.print(value + " ");
}
System.out.println();
}
} finally {
in.close();
}
}

Related

Difficulty writing object to file

I'm trying to read a map of Students from a txt file, after that I add a new student to the map (now is bigger than before) and save it back to the file. After I close my program and reload the data from file, the new students weren't saved.
HashMap<String, Student> studentObj = new HashMap<>(SIZE);
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(DEFAULT_FILE_NAME));
studentObj = (HashMap<String, Student>) in.readObject();
studentObj.put(student.getStudentID(), student);
ObjectOutputStream out;
out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(DEFAULT_FILE_NAME)));
out.writeObject(studentObj);
out.flush();
System.out.println("here " + studentObj.size());
in.close();
out.close();
} catch (IOException e) {
throw new Exception("FILE IS NOT CREATED");
} catch (ClassNotFoundException e) {
throw new Exception("CLASS NOT FOUND EXCPETION");
}
I agree with #xdevs23
Instead of saving the data into arrays (which will use more memory), you could write
/*import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;*/
HashMap<String, Student> studentObj = new HashMap<>(SIZE);
try{
ObjectInputStream in = new ObjectInputStream(new FileInputStream(DEFAULT_FILE_NAME));
studentObj = (HashMap<String, Student>) in.readObject();
studentObj.put(student.getStudentID(), student);
in.close();
System.out.println("here " + studentObj.size());
FileOutputStream fos = new FileOutputStream(DEFAULT_FILE_NAME);
OutputStreamWriter osw = new OutputStreamWriter(fos);
Writer w = new BufferedWriter(osw);
// Iterate using YOUR hash keys
for(int i = 0; i < studentObj.size(); i++){
w.write(studentObj.get(i).getString());
w.write(studentObj.get(i).getStudent());
}
w.close();
catch (IOException e) {
throw new Exception("FILE IS NOT CREATED");
} catch (ClassNotFoundException e) {
throw new Exception("CLASS NOT FOUND EXCPETION");
}
}
And just write the data pulled from ObjectInputStream directly to the file
My code was ok. The problem was that after saving the object to the file, then I closed the app and opened it again. Then, the constructor created a new file that overrides the old one. I added an if statement to create the file just for the first time. I used txt to make it simple and fast because is just a small task. I love to use xml files instead :) And yes, JAVA can save objects.

read an unformatted text file and store its value in hashmap

I have a text file containing data in below format
Vehicle:Bike
MOdel:FZ
Make:
Yamaha
Description
abcdefgh
ijklmn
problems
gear problem, fork bend.
this is auto data
***********************************end***********************
Vehicle:Bike
MOdel:R15
Make:
Yamaha
Description
1234,
567.
890
problems
gear problem, fork bend.
oil leakage
this is auto data
***********************************end***********************
i have given 2 datas but there are many more such in a text file i want to read it and store it in a hashmap such that
Bike:FZ:Yamaha:abcdefghijklmn:gear problem,fork bend.
Bike:R15:Yamaha:1234,567.890:gear problem,fork bend.oil leakage
My sample code:
public static void main(String[] args) {
try {
BufferedReader br = new BufferedReader(new FileReader("data.txt"));
String sCurrentLine;
int i = 0;
int j = 0;
hmap = new HashMap<String, Integer>();
while ((sCurrentLine = br.readLine()) != null) {
System.out.println(sCurrentLine);
sCurrentLine = sCurrentLine.trim();
if (!sCurrentLine.equals("")) // don't write out blank lines
{
if (sCurrentLine.startsWith("***********")) {
i++;
} else {
if (sCurrentLine.startsWith("Vehicle:")) {
String[] veh = sCurrentLine.split(":");
String vehicle = tType[1];
}
if (sCurrentLine.startsWith("Model:")) {
String[] mod = sCurrentLine.split(":");
String model = iShield[1];
}
hmap.put(0,i+":"+vehicle+":"+model);
}
}
j++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
not sure how to read ---> make, description & problems attributes.
You'll need an ObjectInputStream.
An example:
/* Create an ObjectInputStream for your text file
* and a hash map to store the values in. */
ObjectInputStream obj = new ObjectInputStream(new FileInputStream(textFile));
hmap = (HashMap<String, String>) obj.readObject(); // I assume you want strings.
hmap.put("value", var); // Var can be whatever other strings you created.
// It is always a good idea to close streams.
obj.close();
Just remember that, if you need another variable type placed into the hash map, you can create it with something like HashMap<String, byte[]>.
Obviously, you'll need to implement your already-created methods to determine each variable.
If I have not been specific enough, or have missed something important, let me know.

How to deepclone Map with List as value

Is this the best way to deepclone this data structure example: Map<String, List<Object>>?
Map<String, List<Object>> mapB = new LinkedHashMap<String, List<Object>>();
for(String key: mapA.keySet()){
mapB.put(key, new ArrayList<Object>());
mapB.get(key).addAll(mapA.get(key));
}
Thanks for your time.
Is this the best way to deepclone this data structure
More or less, yes. You can make it a bit shorter using the ArrayList constructor that takes a source Collection as argument, and a bit more efficient (but more wordy) by iterating key-value pairs instead of looking up each key again, but it amounts to the same thing.
Map<String, List<Object>> mapB = new LinkedHashMap<>();
for (Map.Entry<String, List<Object>> entry : mapA.entrySet()) {
mapB.put(entry.getKey(), new ArrayList<>(entry.getValue()));
}
It's fine if you don't want deep copy of contaning objects.
An alternative method of deep cloning is to use serialization. The following method shows how this is done:
public Object deepClone(Object obj) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (IOException e) {
return null;
} catch (ClassNotFoundException e) {
return null;
}
}
The apache commons lang SerializationUtils offers a generic method to do this.
Both ways assume that all objects in your object graph implement serializable.

How can I store a data structure such as a Hashmap internally in Android?

In my Android application I'm trying to store a Map structure such as:Map<String, Map<String, String>>using internal storage. I've looked into using SharedPreferences, but as you know, this only works when storing primitive data types. I tried to use FileOutputStream, but it only lets me write in bytes...Would I need to somehow serialize the Hashmap and then write to file?
I've tried reading through http://developer.android.com/guide/topics/data/data-storage.html#filesInternal but I can't seem to find my solution.
Here's an example of what I'm trying to do:
private void storeEventParametersInternal(Context context, String eventId, Map<String, String> eventDetails){
Map<String,Map<String,String>> eventStorage = new HashMap<String,Map<String,String>>();
Map<String, String> eventData = new HashMap<String, String>();
String REQUEST_ID_KEY = randomString(16);
. //eventData.put...
. //eventData.put...
eventStorage.put(REQUEST_ID_KEY, eventData);
FileOutputStream fos = context.openFileOutput(EVENT_FILENAME, Context.MODE_PRIVATE);
fos.write(eventStorage) //This is wrong but I need to write to file for later access..
}
What is the best approach for storing this type of a data structure internally in an Android App? Sorry if this seems like a dumb question, I am very new to Android. Thanks in advance.
HashMap is serializable, so you could just use a FileInputStream and FileOutputStream in conjunction with ObjectInputStream and ObjectOutputStream.
To write your HashMap to a file:
FileOutputStream fileOutputStream = new FileOutputStream("myMap.whateverExtension");
ObjectOutputStream objectOutputStream= new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(myHashMap);
objectOutputStream.close();
To read the HashMap from a file:
FileInputStream fileInputStream = new FileInputStream("myMap.whateverExtension");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Map myNewlyReadInMap = (HashMap) objectInputStream.readObject();
objectInputStream.close();
+1 for Steve P's answer but it does not work directly and while reading I get a FileNotFoundException, I tried this and it works well.
To Write,
try
{
FileOutputStream fos = context.openFileOutput("YourInfomration.ser", Context.MODE_PRIVATE);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(myHashMap);
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
And to Read
try
{
FileInputStream fileInputStream = new FileInputStream(context.getFilesDir()+"/FenceInformation.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Map myHashMap = (Map)objectInputStream.readObject();
}
catch(ClassNotFoundException | IOException | ClassCastException e) {
e.printStackTrace();
}
Writing:
FileOutputStream fos = context.openFileOutput(EVENT_FILENAME, Context.MODE_PRIVATE);
ObjectOutputStream s = new ObjectOutputStream(fos);
s.writeObject(eventStorage);
s.close();
Reading is done in the inverse way and casting to your type in readObject

How to encode a Map<String,String> as Base64 string?

i like to encode a java map of strings as a single base 64 encoded string. The encoded string will be transmitted to a remote endpoint and maybe manipulated by a not nice person. So the worst thing that should happen are invaild key,value-tuples, but should not bring any other security risks aside.
Example:
Map<String,String> map = ...
String encoded = Base64.encode(map);
// somewhere else
Map<String,String> map = Base64.decode(encoded);
Yes, must be Base64. Not like that or that or any other of these. Is there an existing lightweight solution (Single Utils-Class prefered) out there? Or do i have to create my own?
Anything better than this?
// marshalling
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(map);
oos.close();
String encoded = new String(Base64.encodeBase64(baos.toByteArray()));
// unmarshalling
byte[] decoded = Base64.decodeBase64(encoded.getBytes());
ByteArrayInputStream bais = new ByteArrayInputStream(decoded);
ObjectInputStream ois = new ObjectInputStream(bais);
map = (Map<String,String>) ois.readObject();
ois.close();
Thanks,
my primary requirements are: encoded string should be as short as possible and contain only latin characters or characters from the base64 alphabet (not my call). there are no other reqs.
Use Google Gson to convert Map to JSON. Use GZIPOutputStream to compress the JSON string. Use Apache Commons Codec Base64 or Base64OutputStream to encode the compressed bytes to a Base64 string.
Kickoff example:
public static void main(String[] args) throws IOException {
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
String serialized = serialize(map);
Map<String, String> deserialized = deserialize(serialized, new TypeToken<Map<String, String>>() {}.getType());
System.out.println(deserialized);
}
public static String serialize(Object object) throws IOException {
ByteArrayOutputStream byteaOut = new ByteArrayOutputStream();
GZIPOutputStream gzipOut = null;
try {
gzipOut = new GZIPOutputStream(new Base64OutputStream(byteaOut));
gzipOut.write(new Gson().toJson(object).getBytes("UTF-8"));
} finally {
if (gzipOut != null) try { gzipOut.close(); } catch (IOException logOrIgnore) {}
}
return new String(byteaOut.toByteArray());
}
public static <T> T deserialize(String string, Type type) throws IOException {
ByteArrayOutputStream byteaOut = new ByteArrayOutputStream();
GZIPInputStream gzipIn = null;
try {
gzipIn = new GZIPInputStream(new Base64InputStream(new ByteArrayInputStream(string.getBytes("UTF-8"))));
for (int data; (data = gzipIn.read()) > -1;) {
byteaOut.write(data);
}
} finally {
if (gzipIn != null) try { gzipIn.close(); } catch (IOException logOrIgnore) {}
}
return new Gson().fromJson(new String(byteaOut.toByteArray()), type);
}
Another possible way would be using JSON which is a very ligthweight lib.
The the encoding then would look like this:
JSONObject jso = new JSONObject( map );
String encoded = new String(Base64.encodeBase64( jso.toString( 4 ).toByteArray()));
Your solution works. The only other approach would be to serialize the map yourself (iterate over the keys and values). That would mean you'd have to make sure you handle all the cases correctly (for example, if you transmit the values as key=value, you must find a way to allow = in the key/value and you must separate the pairs somehow which means you must also allow this separation character in the name, etc).
All in all, it's hard to get right, easy to get wrong and would take a whole lot more code and headache. Plus don't forget that you'd have to write a lot of error handling code in the parser (receiver side).

Categories