8

The differences between StringBuilder and StringBuffer in Java are well documented and have been touched upon in StackOverflow as well.

Basically, StringBuilder is a non-synchronized copy of StringBuffer, with pretty much the same interface since it was intended as a faster drop-in replacement for StringBuffer. Their API is practically identical and they are actually subclasses of the same inaccessible abstract class in the current JDK.

The one thing I wonder about, therefore, is why they are not publicly related. Having both classes implement a common interface or even having StringBuffer as a subclass of StringBuilder would make sense, allowing the existence of shared code for both classes.

So why this forced separation? Was it so that programmers would not inadvertently mix thread-safe with thread-unsafe code? Or was it just a design oversight that will now be inherited to the end of eternity?

EDIT:

To make things clear: I can speculate on why things are like this, but I am hoping for concrete references to an actual decision, e.g. during a JSR process. Anything that would shed some light on what, to me, is a situation that causes a certain amount of difficulty occasionally.

EDIT 2:

The fact that both classes implement Appendable completely slipped my mind. Probably because that particular interface is useless for most purposes - it can only append a single character or a prepared object and that's it. In most cases it's no more useful than both classes being subclasses of Object.

EDIT 3:

Well, here is the rationale for exactly this question from a semi-official source:

Evaluation by the libraries team:

It is by design that StringBuffer and StringBuilder share no common public supertype. They are not intended to be alternatives: one is a mistake (StringBuffer), and the other (StringBuilder) is its replacement.

Clearly the lack of a common supertype can, in some cases, slow the hoped-for migration from StringBuffer to StringBuilder. The flip side is that by adding a common supertype, we'd be taking the errors of our past and enshrining them in a public interface to be with us for all time. This doesn't merely slow the migration: it derails it.

Community
  • 1
  • 1
thkala
  • 76,870
  • 21
  • 145
  • 185

2 Answers2

3

I dont have a JSR reference but from my exp. below are the few reasons:

  • StringBuffer as a subclass of StringBuilder is not a good idea for performance reasons. As to make StringBuffer thread safe you have to mask every call to StringBuilder which is lot of overhead.

  • Adding to above point, you can further optimize, if you have direct access over the internals of a class that is the reason why Java added java.lang.concurrent over java.util.Collections.synchronized* apis. As more direct access gives more options for optimization. To suport this point Reference from the IBM blog

  • Further adding to first point, I don't think this is a design oversight as both the classes are final so definitely they dont want these classes to be subclassed.

  • Regarding same interfaces, both classes implement the same interfaces i.e Serializable, Appendable, CharSequence. So they are drop-in replacement. The only thing is that they are not implement one common interface but instead three common interfaces. Which make sense, as there is no need to have one bloated interface which technically gonna be sum of current interfaces(Serializable, Appendable, CharSequence).

EDIT:

  • To @MatthewFlaschen point, that there are apis which are same b/w StringBuffer and StringBuilder but not in any of the implemented interfaces. This is more to do with backward compatibility. You want to add a new api but the interface is being used by many other classes, so changing an interface may not be feasible soln. That is a well thought of decision that Java guys might have made. So I wont say its a mistake.

EDIT 2:

SIDE NOTE: Another thing to note is that StringBuffer was introduced in 1.0 and StringBuilder in 1.5. So the apis which are there in both classes but not in interfaces are introduced later and not at the time of creating of these too classes.

havexz
  • 9,262
  • 2
  • 30
  • 28
  • There are methods e.g. append(long) that are in both classes but none of the interfaces. – Matthew Flaschen Mar 19 '12 at 03:45
  • Though I think like how Vector and ArrayList extend AbstractList, StringBuffer and StringBuilder could've extended a similar abstract class. I haven't looked at the latest source, but the StringBuilder seemed to be a straight rip-off of StringBuffer minus the synchronization. – Spencer Kormos Mar 19 '12 at 03:48
  • @MatthewFlaschen append() is from Appendable. – Spencer Kormos Mar 19 '12 at 03:49
  • @SpencerKormos, I specifically said append(long), which is not in `Appendable`. – Matthew Flaschen Mar 19 '12 at 03:52
  • I agree that they may not want to modify Appendable. However, they could make a new interface extending Appendable with the additional methods (e.g. `append(long)`). Like I said in my answer, this would be convenient, but is not essential. – Matthew Flaschen Mar 19 '12 at 03:53
  • 1
    @MatthewFlaschen I had updated my ans. I agree with you that they could have created new interface. But changing/creating an interface is a big decision and definitely needs more thinking. So yes adding same apis is easier as well as has less impact. @SpencerKormos abstract class has the same problem with performance as subclassing bcoz having full access to internals you can optimize more. Example `java.util.CollectionssynchronizedMap` and `java.util.concurrent.ConcurrentHashMap`. – havexz Mar 19 '12 at 03:57
  • "As to make StringBuffer thread safe you have to mask every call to StringBuilder which is lot of overhead." would the overhead be significant compared to the overhead of the actual synchronisation that's already there? (And was deemed significant enough to warrant creating `StringBuilder` in the first place.) – millimoose Mar 19 '12 at 03:59
  • @havexz, I addressed your update. I agree that "changing an interface may not be feasible", but they could still make a new interface extending `Appendable` (and adding `append` methods). – Matthew Flaschen Mar 19 '12 at 03:59
  • 1
    @MatthewFlaschen i already edit my reply..so it is in wrong order...:). @Inerdial well yes it is more overhead (as more synchronized function calls) but the main reason is to have more access to internals. If you have access to internals you can get away with making all calls to `StringBuilder` via `StringBuffer` synchronized. You can optimize by using explicit locks instead of synchronized methods. Exmample, `java.util.Collections. Collections.synchronizedMap ` is exactly doing what you saying and `ConcurrentHashMap` is more optimized. – havexz Mar 19 '12 at 04:07
  • Is there any convenient way via which a variable, field, or parameter field can be declared to be "something which implements both `CharSequence` and `Appendable`"? If not, is there any practical way to make a method accept and store a reference which implements both those interfaces, and be able to use both interfaces on that reference without typecasts? – supercat Mar 18 '14 at 18:35
  • As for the first bullet point, how is there any performance loss by subclassing? At all? – mdenton8 Jul 22 '14 at 07:12
  • Its not performance overhead of subclassing but by subclassing you cannot remove the thread safety overhead from super class like 'synchronized' functions. So you will have the same performance limitation as super class as far as thread safety related performance is concerned. – havexz Jul 23 '14 at 01:24
2

They actually do both implement Appendable.

I don't agree that this is useless. In my experience, a great portion of StringBuilder/StringBuffer usage is just dealing with strings (which implement CharSequence). For the remainder, you can call String.valueOf before passing it in.

It would be convenient if there was another interface that also had the other methods like append(long). But that's not essential.

It would be reasonable to have a common interface. They have different performance and threading characteristics, but that's fine and true of many interfaces in the JDK.

For example, CopyOnWriteArrayList in an array-based thread-safe list (it makes a new list for every write), while LinkedList is a non-thread-safe linked list.

Matthew Flaschen
  • 255,933
  • 45
  • 489
  • 528
  • You didn't answer the question, or even give any insight as to why it might be. – zastrowm Mar 19 '12 at 03:17
  • @MackieChan, I disagree. I said "It would be reasonable to have a common interface.", which is another way of saying, "Yes, it's a design oversight". – Matthew Flaschen Mar 19 '12 at 03:18
  • Honestly, Appendable slipped my mind, mostly because I regularly append numbers e.t.c. in my code. I don't really find that particular interface very useful, because it forces you to convert everything to a string beforehand. Not to mention that the existing content is immutable... – thkala Mar 19 '12 at 03:29