simple StringBuilder proposal

R

Roedy Green

With FastCat, I can write:

sb.append( "<input name=\"cx\" type=\"hidden\" value=\"", cseAccount,
":", cseCode, "\">\n" );

It is just shorthand for

sb.append( "<input name=\"cx\" type=\"hidden\" value=\"",)
sb.append(cseAccount);
sb.append(":");
sb.append(cseCode);
sb.append( "\">\n" );

I propose StringBuilder learn the same trick.
 
A

Andreas Leitgeb

Roedy Green said:
With FastCat, I can write:
sb.append( "<input name=\"cx\" type=\"hidden\" value=\"", cseAccount,
":", cseCode, "\">\n" );
It is just shorthand for
sb.append( "<input name=\"cx\" type=\"hidden\" value=\"",)
sb.append(cseAccount);
sb.append(":");
sb.append(cseCode);
sb.append( "\">\n" );
I propose StringBuilder learn the same trick.

I agree that explicitly calling append only once for
a couple of separate things is fine, as in: eventually
makes code clearer. (Even though your concrete example
doesn't look like supporting my claim, as the line grew
too long.)

Have you tried writing
sb.append( "<input name=\"cx\" type=\"hidden\" value=\"" + cseAccount + ...)
?

I think, I once read here, that the JIT might even be smart enough
to not create a new StringBuilder for the "+"s, but inline it into
the existing StringBuilder.

Have you done timing tests that support your claim that creating
temporary arrays (for the varargs-call) is really faster than simple
"+"-style argument concatenation? If so, how big was the difference?
 
M

markspace

sb.append( "<input name=\"cx\" type=\"hidden\" value=\"", cseAccount,
":", cseCode, "\">\n" );

It is just shorthand for

sb.append( "<input name=\"cx\" type=\"hidden\" value=\"",)
sb.append(cseAccount);
sb.append(":");
sb.append(cseCode);
sb.append( "\">\n" );

I propose StringBuilder learn the same trick.

I think I see what you are saying. Java 8 is "in the chute" *right
now*, so it might be timely to send in an enhancement request. I don't
think they're feature complete yet.

Send some sample code that does what you ask, I think it helps move the
process along and promotes understanding also:

public class StringBuilderUtils {
private StringBuilderUtils() {}

/** Example only, should be an instance method on StringBuilder
* rather than a static method.
*/
public static void append( StringBuilder sb, Object... toAppend ) {
for( Object o : toAppend )
sb.append( o );
}
}

Not tested or even compiled....
 
D

Daniel Pitts

With FastCat, I can write:

sb.append( "<input name=\"cx\" type=\"hidden\" value=\"", cseAccount,
":", cseCode, "\">\n" );

It is just shorthand for

sb.append( "<input name=\"cx\" type=\"hidden\" value=\"",)
sb.append(cseAccount);
sb.append(":");
sb.append(cseCode);
sb.append( "\">\n" );

I propose StringBuilder learn the same trick.
I doubt it is the same. Its probably more likely shorthand for this:

--- snip ---
final String[] hiddenArray = new String[] {
"<input name=\"cx\" type=\"hidden\" value=\"",
cseAccount,
":",
cseCode,
"\">\n"
}

for (String s: hiddenArray) { sb.append(s); }
--- /snip --

Which produces the same end result, but does have some mild overhead of
the array creation. Unless you use that array directly, which I could see.
 
L

Lew

Java already has a shorthand for that:

String s = "<input name=\"cx\" type=\"hidden\" value=\""
+ cseAccount + ":" + cseCode + "\">\n";
I think I see what you are saying. Java 8 is "in the chute" *right
now*, so it might be timely to send in an enhancement request. I don't

Adding a feature that already exists is not an enhancement.
think they're feature complete yet.

It is not the job of the API to provide every conceivable cover method for
every conceivable combination of use cases. Good thing, too, or we programmers
would have no jobs.
 
M

markspace

Java already has a shorthand for that:

String s = "<input name=\"cx\" type=\"hidden\" value=\""
+ cseAccount + ":" + cseCode + "\">\n";

This requires that the compiler actually create a second StringBuilder.
This is not efficient. What Roedy is saying is that given an existing
StringBuilder object, he wants to be able to invoke multiple #append()
calls with out having to spell out each one.
It is not the job of the API to provide every conceivable cover method for
every conceivable combination of use cases.

This is a point. I think that the varagrs method might be notably
inefficient in many cases, as primitives would be autoboxed. However,
it would not hurt to actually investigate this and determine how much
and how often the autoboxing becomes an issue. That investigation I'll
leave to the concerned parties, however.
 
J

Jan Burse

Roedy said:
It is just shorthand for

sb.append("<input name=\"cx\" type=\"hidden\" value=\"");
sb.append(cseAccount);
sb.append(":");
sb.append(cseCode);
sb.append("\">\n");

Since append() returns the StringBuilder itself, you can also write:

sb.append("<input name=\"cx\" type=\"hidden\" value=\"")
.append(cseAccount)
.append(":")
.append(cseCode)
.append("\">\n");

There are a couple of methods in the JDK that are designed this way.

Bye
 
S

Silvio

With FastCat, I can write:

sb.append( "<input name=\"cx\" type=\"hidden\" value=\"", cseAccount,
":", cseCode, "\">\n" );

It is just shorthand for

sb.append( "<input name=\"cx\" type=\"hidden\" value=\"",)
sb.append(cseAccount);
sb.append(":");
sb.append(cseCode);
sb.append( "\">\n" );

I propose StringBuilder learn the same trick.

Ask the Java8 crew to implement something more generally useful that
would allow you to define this for yourself. Google for "pimp my library
in Scala".

Scala allows adding methods to existing classes by means of what are
called implicit conversions. Using structural typing you could even
define an implicit conversion for everything with an append(String)
method to support an append(Any*). This would involve only compiler
magic without an actual wrapper object.
 
S

Silvio

Ask the Java8 crew to implement something more generally useful that
would allow you to define this for yourself. Google for "pimp my library
in Scala".

Scala allows adding methods to existing classes by means of what are
called implicit conversions. Using structural typing you could even
define an implicit conversion for everything with an append(String)
method to support an append(Any*). This would involve only compiler
magic without an actual wrapper object.

Oops, too late. Make that Java9.
 
L

Lew

markspace said:
This requires that the compiler actually create a second StringBuilder.

That's what HotSpot is for, right?
This is not efficient. What Roedy is saying is that given an existing
StringBuilder object, he wants to be able to invoke multiple #append()
calls with out having to spell out each one.

Okay, if that is enough of a hassle, and the "inefficiency" of a second StringBuilder
is really a concern, then you write the method yourself.

The only inefficiency I thought was operating was programmer mindspace efficiency.

Worrying about a second StringBuilder argument is bullshit if you don't have to see it.

It's not like 'StringBuilder#append()' is super-"efficient", now is it? It's gotta convert things
using 'toString()' (oh, hey, there are a few extra objects there anyway, huh?), grow arrays,
and do all kinds of stuff where having a second instance might not even be noticeable.
This is a point. I think that the varagrs method might be notably
inefficient in many cases, as primitives would be autoboxed. However,

There's that strange notion of efficiency again. Primitives are parsed into a String
rep for 'append()' anyway, are they not?
it would not hurt to actually investigate this and determine how much
and how often the autoboxing becomes an issue. That investigation I'll
leave to the concerned parties, however.

And how much additional 'StringBuilder' objects become an issue, and with what
frequency in the programmer community to be worth handling.

I aver that in the end the String '+' operator is fine. Forget micro-optimizing the
StringBuilder. A difference that makes no difference is no difference.
 
R

Robert Klemme

This is a point. I think that the varagrs method might be notably
inefficient in many cases, as primitives would be autoboxed. However,
it would not hurt to actually investigate this and determine how much
and how often the autoboxing becomes an issue. That investigation I'll
leave to the concerned parties, however.

Please don't forget the Object[] creation which is imposed for every
such call.

I cannot really recall what class it was but there is a class which has
an overloaded method with single arguments for the case 1 to 3 and then
has a catch all with vararg. Like this

void f()
void f(Object arg)
void f(Object arg1, Object arg2)
void f(Object arg1, Object arg2, Object arg3)
void f(Object arg1, Object arg2, Object arg3, Object... remainder)

Effective Java seems to have this pattern on page ~200 also.

So someone clearly must have assumed that this can be significant. I'd
say the effect is worst for short argument lists, i.e, if you have only
the vararg method and pass just one argument then the overhead per
instance is highest.

Kind regards

robert
 
J

Joerg Meier

Since append() returns the StringBuilder itself, you can also write:
sb.append("<input name=\"cx\" type=\"hidden\" value=\"")
.append(cseAccount)
.append(":")
.append(cseCode)
.append("\">\n");
There are a couple of methods in the JDK that are designed this way.

@Accessors (chain = true) from the wonderful Lombok does that too, and I
think it's a great pattern for certain cases such as classes with a lot of
fields with sensible defaults, where one usually only wants to change one
or two out of a dozen (but usually different ones).

Liebe Gruesse,
Joerg
 
J

Jan Burse

Joerg said:
@Accessors (chain = true) from the wonderful Lombok does that too, and I
think it's a great pattern for certain cases such as classes with a lot of
fields with sensible defaults, where one usually only wants to change one
or two out of a dozen (but usually different ones).

Liebe Gruesse,
Joerg


Do you mean the google builder pattern? It makes also use of it:

http://www.javacodegeeks.com/2012/07/builder-design-pattern-in-java.html

Bye
 
M

markspace

I cannot really recall what class it was but there is a class which has
an overloaded method with single arguments for the case 1 to 3 and then
has a catch all with vararg. Like this

EnumSet, and there's five of them, not three. It's a rather extreme
case of "let's make this as fast as possible, even to compile directly
to one machine instruction" rather than a general heuristic.

<http://docs.oracle.com/javase/7/docs/api/java/util/EnumSet.html>
 
S

Sven Köhler

Hi,

Am 26.02.2013 12:53, schrieb Roedy Green:
With FastCat, I can write:

sb.append( "<input name=\"cx\" type=\"hidden\" value=\"", cseAccount,
":", cseCode, "\">\n" );

So I googled and found a FastCat class that has append(String ...) and
append(Object ...) methods. And just by coincidence, you're a co-author
of that class.

So basically, whatever arguments you pass, they are first wrapped in an
array, and then passed to the append method. To make things worse, if
you'd pass a mix of char, int, double, etc. - some of these things would
first be wrapped in Char, Integer, Double, etc. - and then the
toString() method is called.
It is just shorthand for

sb.append( "<input name=\"cx\" type=\"hidden\" value=\"",)
sb.append(cseAccount);
sb.append(":");
sb.append(cseCode);
sb.append( "\">\n" );

No it is not.
I propose StringBuilder learn the same trick.

So, are you aware of the performance hit? Are you trying to say, that
you prefer convenience over performance here? And if you don't care
about performance in this particular case, why don't you use the +
operator for Strings which is pretty convenient? Or why don't you
exploit the fact that append() returns the StringBuilder instance?

I'm not sure, what to make of your posting. Are you just trying to
promote the FastCat class?

Would you happen to have a benchmark, where you append lots of integers
to a FastCat and a StringBuilder? I would love to see what happens if
you use the append(Object...) method for that.


Regards,
Sven
 
J

Joerg Meier

Am 26.02.2013 12:53, schrieb Roedy Green:
So I googled and found a FastCat class that has append(String ...) and
append(Object ...) methods. And just by coincidence, you're a co-author
of that class.

Shocking, Roedy advertising for himself.
So, are you aware of the performance hit? Are you trying to say, that
you prefer convenience over performance here? And if you don't care
about performance in this particular case, why don't you use the +
operator for Strings which is pretty convenient? Or why don't you
exploit the fact that append() returns the StringBuilder instance?
I'm not sure, what to make of your posting. Are you just trying to
promote the FastCat class?
Would you happen to have a benchmark, where you append lots of integers
to a FastCat and a StringBuilder? I would love to see what happens if
you use the append(Object...) method for that.

Because his incessant advertising irritates me, I spent a few minutes
writing a small benchmark, comparing the performance of StringBuilder,
StringBuffer and FastCat. The results were ... well, pretty much what one
would expect:

Took 00:08.845ms to append 454,895,082 chars total with FastCat.
Took 00:03.939ms to append 454,895,082 chars total with StringBuffer.
Took 00:03.462ms to append 454,895,082 chars total with StringBuilder.

So FastCat is about 2.3 times slower than the Oracle/Sun implementation of
StringBuffer, and 2.6 times slower than the unsychronized StringBuilder.
Shocker.

Not wanting to be too harsh on Roedy, seeing as how he's probably freed
Europe during WWII, I figured I'd run the benchmark again, but instead of
instantiating millions of StringB*S/FastCatS, I'd reuse the same one.
Maybe, after all, it was the object creation that caused the difference.

Hilariously, at this point, I started getting exceptions thrown into my
face - FastCat doesn't support fancy things like appending more than 1000
chars or so. And instead of signalling that with a proper error code, it
abuses ArrayIndexOutOfBoundsException to signal that it ... refuses to
resize itself ?

Don't get me wrong, it's not a bug causing that, he actually explicitly
throws that exception when his buffer gets full, outright refusing to
resize the object. Because terminating with an exception is better for
performance ... right ?

/**
* We overflowed the array. Give up. Get user to improve estimate. We
could recover automatically,
* but that would defeat the intent of fast code.
*/

Couldn't make this up if I tried. I decided to FastCat's metaphorical arm
over my shoulder and just told it to make more buffer space in the
constructor, and this is the glorious outcome of reusing the same SB/FC
object:

Took 00:14.763ms to append 454,895,082 chars total with FastCat.
Took 00:03.384ms to append 454,895,082 chars total with StringBuffer.
Took 00:02.825ms to append 454,895,082 chars total with StringBuilder.

At this point, I vote that Roedy has to rename it to SlowCat.

Test code (ugly, but too lazy to clean it up):

<http://pastebin.com/TNj7Fmqy>

Note that you need plenty of RAM if you want to keep all those bytes in
memory. I used -Xmx4G -Xms4G, though you can probably make due with less.

ps.: When I just went to post the code, I noticed that by accident I didn't
actually *USE* Roedys advanced syntax. Instead, as with the SB classes, I
used multiple .append()s. Talk about embarassing, luckily I noticed before
I pressed send.

I was about ready to delete this whole post, thanking the good spirits of
Usenet for saving me from the shame of publically embarassing myself when
it turned out that a small change to his advanced syntax would speed
FastCat up by leaps and bounds.

No. In fact, his advanced syntax is *EVEN* *SLOWER*: 6.4 times slower than
StringBuilder, to be exact.

Took 00:18.016ms to append 454,895,082 chars total with FastCat.

Liebe Gruesse,
Joerg
 
M

markspace

No. In fact, his advanced syntax is *EVEN* *SLOWER*: 6.4 times slower than
StringBuilder, to be exact.

But only 22% slower than FastCat#append, which suggests to me that
varargs are acceptable where performance is not an issue, and
convenience is.

(Although, FastCat's slow performance might be masking the the true
overhead of varargs. But it doesn't seem to me to be fair to call
varargs a 640% slowdown by itself.)

Good job actually sussing this out. You did far more work with a
questionable library than I was willing to.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,225
Latest member
Top Crypto Podcasts

Latest Threads

Top