How to serialize a HashMap with BufferedOutputStream in Java? - java

I have a very large HashMap of the format HashMap<String, List<String>>, and I want to serialize it using BufferedOutputStream because I think that it will be more efficient than with a regular OutputStream.
But how do I divide the HashMap in chunks of the size of the buffer? Should I just iterate through the HashMap?

If you plan to write into a local file you need to chain FileOutputStream, BufferedOutputStream and ObjectOutputStream. With below setup BufferedOutputStream should minimize direct writes to the file system using default buffer of 8192 bytes.
Map<String, List<String>> data = new HashMap<>();
data.put("myKey", List.of("A", "B", "C"));
File outFile = new File("out.bin");
try (FileOutputStream fos = new FileOutputStream(outFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(data);
oos.flush();
}
Unless the output file is too big there is no need for further chunking.

Related

Java OutStreamWriter to ByteArrayInputStream

I am writing a csv file in a very old java application so i can not use all the new Java 8 streams.
Writer writer = new OutputStreamWriter(new FileOutputStream("file.csv"));
writer.append("data,");
writer.append("data,");
...
Then I need to transform the writer object into a ByteArrayInputStream.
How can i do it ?
Thanks in advance.
Best regards.
This depends on what you are trying to do.
If you are writing a bunch of data to the file and THEN reading the file you will want to use a FileInputStream in place of your ByteArrayInputStream.
If you want to write a bunch of data to a byte array then you should take a look at using a ByteArrayOutputStream. If you then need to read the byte array as a ByteArrayInputStream you can pass the ByteArrayOutputStream into the input stream like what is shown below. Keep in mind this only works for writing and THEN reading. You can not use this like a buffer.
//Create output stream
ByteArrayOutputStream out = new ByteArrayOutputStream();
//Create Writer
Writer writer = new OutputStreamWriter(out);
//Write stuff
...
//Close writer
writer.close();
//Create input stream using the byte array from out as input.
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
Short answer: you can't.
A ByteArrayInputStream is just not assignable from a OutputStreamWriter.
Since you're probably after write, you can just read the file back to a byte[] and then construct a ByteArrayInputStream with it:
File file = new File("S:\\Test.java");
FileInputStream fis = new FileInputStream(file);
byte[] content = new byte[(int) file.length()];
fis.read(content,0,content.length);
ByteArrayInputStream bais = new ByteArrayInputStream(content);

Stream<Object> to InputStream

How do i convert type
Stream<Object> into an InputStream? Currently, I get the iterator and loop through all of the data converting it to a byteArray and adding it to an inputStream:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
Iterator<MyType> myItr = MyObject.getStream().iterator();
while (myItr.hasNext()) {
oos.writeObject(myItr.next().toString()
.getBytes(StandardCharsets.UTF_8));
}
oos.flush();
oos.close();
InputStream is = new ByteArrayInputStream(bao.toByteArray());
What is the overhead of doing this though? If my stream contains a terabyte of data, wouldn't I be sucking a terabyte of data into memory? Is there any better way to achieve this?
You should be able to convert the OutputStream into an InputStream using a pipe:
PipedOutputStream pos = new PipedOutputStream();
InputStream is = new PipedInputStream(pos);
new Thread(() -> {
try (ObjectOutputStream oos = new ObjectOutputStream(pos)) {
Iterator<MyType> myItr = MyObject.getStream().iterator();
while (myItr.hasNext()) {
oos.writeObject(myItr.next().toString()
.getBytes(StandardCharsets.UTF_8));
}
} catch (IOException e) {
// handle closed pipe etc.
}
}).start();
Inspired by this answer.
Would this work for you?
https://gist.github.com/stephenhand/292cdd8bba7a452d83c51c00d9ef113c
It's an InputStream implementation that takes a Stream<byte[]> as input data. You just need to .map() your abitrary objects to byte arrays however you want each object to be represented as bytes.
It only calls a terminal operation on the Stream when the InputStream is read, pulling objects off the Stream as the consumer reads more of the InputStream so it never loads the whole set into memory

Create HashMap with filename as key and filecontent as value

I have few files in my local folder. I want to store the file-names as the key and the content of the corresponding file as value.
HashMap<String,String> hm = new HashMap<String,String>();
hm.put(filename,filecontent);
Can someone tell me is this the right way to do?
When storing file contents as a String, you have to make sure the encoding is respected, I would recommend to use byte array instead:
Map<String, byte[]> hm = new HashMap<String, byte[]>();
Also: depending on how many files you are manipulating, you may want to consider using file streams to avoid keeping everything in memory.
There are a couple of steps to what you would like.
I am going to assume you have the filename already as a String.
HashMap<String, byte[]> hm = new HashMap<String, byte[]>(); //Initialize our hashmap with String as key, and byte array as data (binary data)
FileInputStream fileStream = new FileInputStream(filename); //Get a stream of the file
byte[] buff = new byte[512]; //A buffer for our read loop
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); //Where to write buff content to, we can convert this into the output byte array with toByteArray()
while(fileStream.read(buff) > 0) { //read 512 bytes of file at a time, until end of file
byteStream.write(buff); //write buff content to byte stream
}
fileStream.close(); //Close our file handle, if we don't do this we may not be able to see changes!
hm.put(filename, byteStream.toByteArray()); //insert filename and filecontent to hashmap
As others have suggested, however, this is less than ideal. You are holding multiple files in memory for an arbitrary length of time. You can eat a lot of ram and not realize it doing this, and quickly run into an out of memory exception.
You would be better off reading the file content only when needed, so there isn't a whole file sitting in your ram for god knows how long. The only plausible reason I could see to store file contents would be if you were reading it a lot, and you could afford the ram to cache the file in memory.
Update for Binary Data
HashMap<String,String> hm = new HashMap<String, byte[]>();
final File folder = new File("/home/you/Desktop");
listFilesForFolder(folder);
public void listFilesForFolder(final File folder) {
for (final File fileEntry : folder.listFiles()) {
if (fileEntry.isDirectory()) {
listFilesForFolder(fileEntry);
} else {
String name = fileEntry.getName();
byte[] fileData = new byte[(int) fileEntry.length()];
DataInputStream dis = new DataInputStream(new FileInputStream(fileEntry));
dis.readFully(fileData);
dis.close();
hm.put(name,fileData);
}
}
}
Tested for Zip file for OP:
public static void main(String[] args) throws FileNotFoundException, IOException {
File file = new File("D:\\try.zip");
System.out.println(file.length());
byte[] fileData = new byte[(int) file.length()];
DataInputStream dis = new DataInputStream(new FileInputStream(file));
dis.readFully(fileData);
dis.close();
}

Is there an alternative to FileOutputStream type , which does not create a file?

To process some images in my android application I currently use code like this:
FileOutputStream fileOuputStream = new FileOutputStream(imgpath);
[..DO SOME STUFF..]
Bitmap data = BitmapFactory.decodeByteArray(bFile, 0, bFile.length, options);
data.compress(Bitmap.CompressFormat.JPEG, 90, fileOuputStream);
[..DO SOME STUFF..]
File file = new File(imgpath);
FileInputStream imageInFile = new FileInputStream(file);
byte imageData[] = new byte[(int) file.length()];
imageInFile.read(imageData);
[..DO SOME STUFF..]
file.delete();
//NOTE: The code is all in the same method
the problem is that passing my image from one part of the code to another using this method creates a temporary file.
I was looking for a way to read / write the file data using a memory variable, something like "generic stream" in which store data in order to replace use of "FileInputStream " and "FileOutputStream " and do not write temporary file.
If you are able to use an InputStream or OutputStream you can use ByteArrayInputStream or ByteArrayOutputStream for in memory handling of the data.
If you have two thread you can also use PipedInputStream and PipedOutputStream together to communicate between the threads.
You could write your data to a ByteArrayOutputStream and use the byte array of that stream:
ByteArrayOutputStream out = new ByteArrayOutputStream();
data.compress(Bitmap.CompressFormat.JPEG, 90, out);
// now take the bytes out of your Stream
byte[] imgData = out.toByteArray();

Serialize object with outputstream

Suppose I have an OutputStream (and not an ObjectOutputStream). Is is possible to send a serialized object using the write method? Thanks!
Here is what you do to serialize the object:
new ObjectOutputStream(outputStream).writeObject(obj);
If you want to control the byte[] output:
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(buffer);
oos.writeObject(obj);
oos.close();
byte[] rawData = buffer.toByteArray();
You could use ObjectOutputStream to 'capture' the objects data in a byte Array and send this to the OutputStream.
String s = "test";
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
oos.writeObject( s );
byte[] byteArray = baos.toByteArray();
for ( byte b : byteArray ) {
System.out.print( (char) b );
}
Another non generic option would be to serialize the object in a string representation e.g. CSV
This is trivial: you can simply wrap your original OutputStream in a new ObjectOutputStream, and then use the specialized methods of ObjectOutputStream:
OutputStream myOriginalOutputStream = ...;
ObjectOutputStream oos = new ObjectOutputStream(myOriginalOutputStream);
oos.writeObject(new MyObject());
oos.flush();
oos.close();
Internally, ObjectOutputStream will call the underlying OutputStream's write() method.
You must have to use ObjectOutputStream class and its methods to *serialize* objects. In fact ObjectOutputStream is a sub-class of java.io.OutputStream (It is an abstract super class of byte-oriented streams). Take a look at an article on Java Serialization API.
EDIT:
You can use XMLEncoder
(from the Doc : The XMLEncoder class is a complementary alternative to
the ObjectOutputStream and can used to generate a textual
representation of a JavaBean in the same way that the
ObjectOutputStream can be used to create binary representation of
Serializable objects)

Categories

Resources