61

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: 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.

Olcay Ertaş
  • 5,987
  • 8
  • 76
  • 112
CPerkins
  • 8,968
  • 3
  • 34
  • 47
  • 6
    You should not use `builder.append("first thing" + newline)`. The problem is that there's an extra String allocation which is not needed (it first creates "first thing\n" and then copies the string data to the builder). Instead, do it like this: `builder.append("first thing").append(newline)`. This directly copies `"first thing"` to the builder and then `newline`. Of couse, StringBuilder should only be used if the number of things is not constant (e.g. it's in a loop), javac already optimizes `"a" + "b" + "c"` into `new StringBuilder().append("a").append("b").append("c").toString()`. – robinst Feb 28 '12 at 14:20
  • @robinst Apologies for the very slow response. I just revisited this question and noticed your comment. Excellent point, and I've redone my test. See edits. – CPerkins Jan 11 '13 at 22:13
  • 5
    `Throwable.printStackTrace()` requires a writer. – ceving Apr 25 '13 at 17:06
  • 1
    I am surprised that the OP accepted answer for 'stylistic' reasons. Alan Moore's explanation is more than satisfactory. I have actually come across a scenario where i was forced to use StringWriter because the API (third party API) expects Writer instance and i just wanted to write the contents to the string rather than file/stdout. It really doesn't matter if StringWriter internally uses Stringbuffer/Stringbuilder as long as the writer is not shared with multiple threads. – srikanth yaradla May 28 '13 at 13:03

7 Answers7

43

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.

Eric
  • 6,563
  • 5
  • 42
  • 66
Alan Moore
  • 73,866
  • 12
  • 100
  • 156
  • Thanks, and that does make sense, but I've found this widely used in a codebase written by a set of very smart people - actually, on average, they seem to be the smartest I've ever worked with. So I'm looking for reasons they might have done it. – CPerkins Jun 05 '10 at 16:24
  • 3
    This should be the accepted answer to this question. I have actually come across a scenario where i was forced to use StringWriter because the API (third party API) expects Writer instance and i just wanted to write the contents to the string rather than file/stdout.@Alan simply nailed it when he said "You use StringWriter when you have to". – srikanth yaradla May 28 '13 at 12:56
  • 2
    I've come across a scenario where I was forced to use `StringWriter` because the API _of_ _the_ `java.lang.Throwable` _class_ does not have any methods that receive a `StringBuilder` as an argument when the stack trace of an exception has to be printed (there are only methods that accept a [`PrintStream`](http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#printStackTrace%28java.io.PrintStream%29) or [`PrintWriter`](http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#printStackTrace%28java.io.PrintWriter%29)). –  May 29 '13 at 23:08
  • To be clear, it's a compromise because you wanted to use a StringBuilder (assuming no multiple threads involved) which would have been more clean and efficient. – sactiw Jul 27 '17 at 11:55
21

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.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
20

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.

CPerkins
  • 8,968
  • 3
  • 34
  • 47
  • Results are not surprising. Assuming that any particular call to `write(...)` on `StringWriter` simply executes one matching call to `append(...)` on the internal `StringBuilder`, this is exactly the kind of boilerplate that the Java JIT is designed to optimize away. – jdmichal Nov 13 '14 at 21:27
  • 1
    @jdmichal `StringWriter` uses a **`StringBuffer`**, which is the `synchronized` alternative to the `StringBuilder`. So the JIT also needs to removed unnecessary locks - which it is much better at doing these days than it used to be. Still, the problem is non-trivial and the similarity if performance is perhaps a little more surprising that you might think... – Boris the Spider Apr 08 '16 at 10:52
  • In relative terms, your test shows StringWriter to be **6%** slower than StringBuilder (because it uses a synchronized StringBuffer internally and because of its own overhead), while the bad newline append is **17%** slower than correct StringBuilder usage. Granted, the time taken is not twice or more, but I wouldn't say "irrelevant", especially if you're handling large amounts of data. Also, Hello almost 10 year old thread :-) – Tobia Feb 24 '19 at 09:27
16

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.

krock
  • 28,904
  • 13
  • 79
  • 85
  • Thanks, yes, I understand what they each do. But I've come across this new (to me) idiom in code written by some very smart people, and I'm wondering if they had a good reason to prefer it. – CPerkins Jun 05 '10 at 15:12
  • OK, no problem I have added a conclusion section to the answer. – krock Jun 05 '10 at 15:40
  • So your reason is basically stylistic: it relies on the notion that StringBuilder's **intention** is to manipulate strings, while the ability to do so with the Writers is a side effect. – CPerkins Jun 05 '10 at 16:28
7

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.

pjbarnes
  • 71
  • 1
  • 2
  • Good thoughts. I'm not as worried about having to call `System.getProperty()`, as it's a one-time cost which amortizes to zero if, as you say, you're writing a lot of code. Definitely a good point about having to `+ newline` with `StringBuilder`, though. – CPerkins Oct 23 '10 at 14:27
2

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."

omid haghighatgoo
  • 322
  • 1
  • 3
  • 16
0

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
A248
  • 690
  • 7
  • 17