String assembly by StringBuilder vs StringWriter and PrintWriter

Java

Java Problem Overview


I recently encountered an idiom I haven't seen before: string assembly by StringWriter and PrintWriter. I mean, I know how to use them, but I've always used StringBuilder. Is there a concrete reason for preferring one over the other? The StringBuilder method seems much more natural to me, but is it just style?

I've looked at several questions here (including this one which comes closest: https://stackoverflow.com/questions/602279/stringwriter-or-stringbuilder ), but none in which the answers actually address the question of whether there's a reason to prefer one over the other for simple string assembly.

This is the idiom I've seen and used many many times: string assembly by StringBuilder:

public static String newline = System.getProperty("line.separator");
public String viaStringBuilder () {
   StringBuilder builder = new StringBuilder();
   builder.append("first thing").append(newline);  // NOTE: avoid doing builder.append("first thing" + newline);
   builder.append("second thing").append(newline);
   // ... several things
   builder.append("last thing").append(newline);
   return builder.toString();
}

And this is the new idiom: string assembly by StringWriter and PrintWriter:

public String viaWriters() {
   StringWriter stringWriter = new StringWriter();
   PrintWriter printWriter = new PrintWriter(stringWriter);
   printWriter.println("first thing");
   printWriter.println("second thing");
   // ... several things
   printWriter.println("last thing");
   printWriter.flush();
   printWriter.close();
   return stringWriter.toString();
}

Edit It appears that there is no concrete reason to prefer one over the other, so I've accepted the answer which best matches my understanding, and +1ed all the other answers. In addition, I posted an answer of my own, giving the results of the benchmarking I ran, in response to one of the answers. Thanks to all.

Edit again It turns out that there is a concrete reason to prefer one (specifically the StringBuilder) over the other. What I missed the first time was the addition of the newline. When you add a newline (as above, as a separate append), it's slightly faster - not hugely, but coupled with the clarity of intent, it's definitely better. See my answer below for the improved timings.

Java Solutions


Solution 1 - Java

StringWriter is what you use when when you want to write to a string, but you're working with an API that expects a Writer or a Stream. It's not an alternative, it's a compromise: you use StringWriter only when you have to.

Solution 2 - Java

Stylistically, the StringBuilder approach is cleaner. It is fewer lines of code and is using a class that was specifically designed for the purpose of building strings.

The other consideration is which is more efficient. The correct way to answer that would be to benchmark the alternatives, but (based on the source code) I would expect StringBuilder to be faster.

A StringWriter uses a StringBuffer (rather than a StringBuilder) under the hood to hold the characters written to the "stream". Thus using a StringWriter for string assembly is going to incur StringBuffer's synchronization overhead, whether your application needs it or not. But if your application does need the synchronization, then you should consider using a StringBuffer directly.


CPerkins has done some benchmarking (see his answer) and his results align with my intuition. They say that the difference between optimal StringBuilder vs StringWriter is ~5% which is likely to be insignificant for a typical application1. However, it is nice to know that the "style" and "performance" criteria are giving the same answer!

1 - While a typical application doesn't spend most of its time assembling strings, there are exceptions. However, one should not spend time optimizing this kind of thing based purely on guesswork. Profile your code first.

Solution 3 - Java

Okay, since the answers seem to stress the stylistic reasons for preferring one over the other, I decided to gen up a timing test.

Edit: Following robinst's comment above, I now have three methods: one which does PrintWriter(StringWriter)-style appending, and two which use StringBuilder: one with the append of the newline inside the append (as in the original: builder.append(thing + newline);), and the other does a separate append (as above: builder.append(thing).append(newline);).

I followed my usual benchmarking scheme: call the methods several (1000 in this case) times first to give the optimizer time to warm up, then call each a large number of times (100,000 in this case) in alternating sequence, so that any changes in the workstation's runtime environment are more fairly distributed, and run the test itself several times and average the results.

Oh, and of course the number of lines created and the length of the lines is randomized but held to be the same for each pair of calls, because I wanted to avoid any possible effects of buffer size or line size on the results.

The code for this is here: http://pastebin.com/vtZFzuds

Note: I have not yet updated the pastebin code to reflect the new test.

TL,DR? The average results for 100,000 calls to each method are quite close:

  • 0.11908 ms per call using StringBuilder (+newline inside the paren)
  • 0.10201 ms per call using StringBuilder (newline as a separate append)
  • 0.10803 ms per call using PrintWriter(StringWriter)

That's so close that the timing is nearly irrelevant to me, so I'm going to continue doing things the way I always did: StringBuilder(with separate append), for stylistic reasons. Of course, while working in this established and mature codebase, I'll mostly keep to the existing conventions, in order to minimize surprise.

Solution 4 - Java

Writers

  • PrintWriter writes to a stream. It does weird things like suppressing IOExceptions. System.out is one of these.
  • StringWriter is a Writer that writes to a StringBuffer (similar to ByteArrayOutputStream for OutputStreams)

StringBuilder

  • and of course StringBuilder is what you want if you are simply want to constructs strings in memory.

Conclusion

Writers by design are meant to push character data out a stream (usually to a file or through a connection) so being able to use Writers to simply assemble strings seems to be a bit of a side effect.

StringBuilder (and its synchronized sibling StringBuffer) are designed to assemble strings (read string bashing). If you look at the StringBuilder API you can see thatnot only can you do vanilla appending but also replace, reverse, insert etc. StringBuilder is your String construction friend.

Solution 5 - Java

I see two advantages of the PrintWriter method: (1) You don't have to add " + newline" at the end of every single line, which actually results in shorter code if you have a lot of writing to do. (2) You don't have to call System.getProperty(); you let the PrintWriter worry about what the newline character should be.

Solution 6 - Java

in this link : http://nohack.eingenetzt.com/java/java-stringwriter-vs-stringbuilder the author shows that the StringWriter is even slightly faster than the StringBuilder.and also said : "So when bigger amounts of data come into play the StringWriter clearly shows its superiority in performance over the StringBuilder."

Solution 7 - Java

Looking at the implementation of StringWriter in JDK 11, it uses a StringBuffer underneath. StringBuffer is a synchronized version of StringBuilder, as described in this question on StringBuilder vs. StringBuffer

Considering this, StringBuilder can be slightly faster. So if you are writing performance-sensitive code, prefer StringBuilder unless you need synchronization.

In my case, I had to choose between using a StringBuilder or a StringWriter as an Appendable, and I did not need the synchronization of StringWriter/StringBuffer.

To summarize:

  • StringBuilder - unsynchronized in-memory appending

  • StringBuffer - synchronized in-memory appending

  • StringWriter - wraps StringBuffer, also extends Writer

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCPerkinsView Question on Stackoverflow
Solution 1 - JavaAlan MooreView Answer on Stackoverflow
Solution 2 - JavaStephen CView Answer on Stackoverflow
Solution 3 - JavaCPerkinsView Answer on Stackoverflow
Solution 4 - JavakrockView Answer on Stackoverflow
Solution 5 - JavapjbarnesView Answer on Stackoverflow
Solution 6 - Javaomid haghighatgooView Answer on Stackoverflow
Solution 7 - JavaA248View Answer on Stackoverflow