Writing HashMap contents to the file
Solution 1
The simplest, non-copying, most “streamish” solution is
Files.write(mOutputPath, () -> mHashMap.entrySet().stream()
.<CharSequence>map(e -> e.getKey() + DATA_SEPARATOR + e.getValue())
.iterator());
While a Stream does not implement Iterable
, a lambda expression performing a Stream operation that ends with calling iterator()
on the stream, can be. It will fulfill the contract as the lambda expression will, unlike a Stream, produce a new Iterator
on each invocation.
Note that I removed the explicit UTF-8
character set specifier as java.nio.Files
will use UTF-8
when no charset is specified (unlike the old io classes).
The neat thing about the above solution is that the I/O operation wraps the Stream processing, so inside the Stream, we don’t have to deal with checked exceptions. In contrast, the Writer
+forEach
solution needs to handle IOException
s as a BiConsumer
is not allowed to throw checked exceptions. As a result, a working solution using forEach
would look like:
try(Writer writer = Files.newBufferedWriter(mOutputPath)) {
mHashMap.forEach((key, value) -> {
try { writer.write(key + DATA_SEPARATOR + value + System.lineSeparator()); }
catch (IOException ex) { throw new UncheckedIOException(ex); }
});
} catch(UncheckedIOException ex) { throw ex.getCause(); }
Solution 2
You can simply avoid using a List<String>
by directly writing out the lines to disk using e.g. a Writer
:
Writer writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(new File(mOutputPath)), StandardCharsets.UTF_8));
mHashMap.forEach((key, value) -> writer.write(key + DATA_SEPARATOR + value + System.lineSeparator()));
writer.flush();
writer.close();
Solution 3
You could map the entries of the map to a string and write them to a FileChannel
. The additional methods simply do the exception handling so the stream operations become more readable.
final Charset charset = Charset.forName("UTF-8");
try(FileChannel fc = FileChannel.open(mOutputPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)) {
mHashMap.entrySet().stream().map(e -> e.getKey() + ":::" + e.getValue() + "\n")
.map(s -> encode(charset, s))
.forEach(bb -> write(fc, bb));
}
void write(FileChannel fc, ByteBuffer bb){
try {
fc.write(bb);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
ByteBuffer encode( Charset charset, String string){
try {
return charset.newEncoder().encode(CharBuffer.wrap(string));
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
}
}
Comments
-
Helisia almost 2 years
I have a
HashMap<Integer, Integer>
. I write its content to the file, so each line of it containshashmapKey:::hashmapValue
. This is how I do it now:List<String> mLines = new ArrayList<String>(); mHashMap.forEach((key, value) -> mLines.add(key + DATA_SEPARATOR + value)); Files.write(mOutputPath, mLines, StandardCharsets.UTF_8);
I very doubt that I need to copy entire
HashMap
to the list of strings, I am sure it will give me performance issues when working with big amounts of data. My question is: how can I writeHashMap
contents to the file using Java 8 avoiding copying values in another list? -
Holger about 8 yearsYou should use try-with-resource.
-
Holger about 8 yearsBesides that, this solution doesn’t work as
IOException
s potentially thrown byWriter.write
must be catched (which will dramatically complicate the lambda expression)… -
Tagir Valeev about 8 yearsNote that (while it's sane assumption) it's not specified that
Files.write
traverses theIterable
only once. -
Robert about 8 yearsI omitted a try-catch or finally environment intentional as it is just a code snippet showing how to do it an not a full-featured program.
-
Holger about 8 years@Tagir Valeev: It’s also within the specification if the JRE reuses the lambda instance (assuming that it captures the same
Map
instance) on multiple executions of this code. That’s another reason to ensure correctly producing a new iterator on each invocation. -
Holger about 8 years@Robert: you should really read the linked article. A try-with-resource statement doesn’t need a
finally
clause and is, in fact, shorter than your code. -
Robert about 8 years@Holger I know try-with-resource. As it is equal to a try -finally block I did not mention it separately.
-
Holger about 8 yearsIf you know it, you should know that it allows to omit the explicit
close()
invocation (theflush()
is obsolete anyway), so again, it would make your code simpler, so there is no reason not to use it in an answer. Your code is more complicated than necessary while failing to ensure safe closing. Besides that, it is not equal to closing in afinally
block, there is a fundamental semantic difference. -
Jagesh Maharjan almost 6 yearsIs there a way to append a file with stream.
-
Robert almost 6 years@Jagesh Maharjan That is independent of the stream API. The
FileOutputStream
has a constructor which takes aboolean
as second argument. If you set this totrue
it will append the data to an existing file. -
Jagesh Maharjan almost 6 years@Robert, I was supposed to Comment on the previous Answer but commented here. Sorry for the ambiguity. Thanks.