JDK-8357183 : Improving efficiency of Writer::append(CharSequence) and Writer::append(CharSequence, int, int)
  • Type: Sub-task
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 25
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2025-05-17
  • Updated: 2025-05-19
Description
This sub task of JDK-8356679 proposes implementation-only changes to java.io.Writer. No API is changed. Implementation behavior is slightly changed w.r.t to self-calls to non-private methods.

The target is to make the Writer::append(CharSequence) and Writer::append(CharSequence, int, int) methods *in the non-String* case be as efficient as they are already *in the String* case.

This is achived by now handling CharSequence in *the exact same* optimized way as String was specifically handled before. This generalization of the previously String-specific optimization is possible since Java 25, thanks to the new CharSequence::getChars(int, int, char[], int) bulk-read method.

Prior to the proposed change, the code was unefficient, as ontop of what the String-case did, the text content of other CharSequences (like StringBuilder and CharBuffer) had to be copied *once or multiply*, before it was finally processed *as* a String.

The changes in detail are:
* Extract the original implementation of Writer::write(String, int, int) to become a newly introduced, internal method Writer::implWrite(CharSequence, int, int). This allows making use of that exact original implementation by other methods, particularly CharSequence::append, but potentially also sub classes in the same package (for subsequent enhancement of specific Writers).
* Writer::append(CharSequence) prevents the previously implied creation of an intermediate String representation in the non-String case (which implied creating another temporary object on the heap, and duplicating full text content), ontop of what the String case did
* Writer::append(CharSequence, int, int), in addition to the optimization of the single-arg case, also prevents the previously implied creation of a sub sequence (which always meant to create another temporary object on the heap, and in many cases meant to duplicate partial text content).
* As a benefit, the JavaDocs of these methods can be simplified thanks to the reduced number of self-calls. While this changes the internal behavior slightly, it is finally clarifying that these JavaDoc sections were originally meant as explanatory notes what *the default code* does, but not as as mandatory specs what *subclasses* have to do.

This work is foundational to subsequent enhancements of specific Writers, as the new implWrite(CharSequence, int, int) method is to be called by them *instead* of the previously called write(String, int, int) method, to allow for the same efficiency optimiziation now found in the new default code. Due to that, no other Writers are changed in this first step; these Writers will follow in subsequent JBS / PRs.
Comments
>These sounds like incompatible changes to me. The Writer (and related) classes were introduced in JDK 1.1 and there are an unknown number of subclasses out in the wild that depend on the exact behavior of the implementations in this class. Changing the behavior of these methods could break such code. The fact that the behavior of the method implementations isn't specified doesn't give us license to change their behavior arbitrarily, even if the results still conform to the specs. I'd prefer to see changes that don't result in any externally visible behavior changes. Agreed and understood in general, nevertheless we need to discuss the exact change planned, instead of the general case. The append() methods were added in JDK 1.5 and since then are simple aliases for write(), with no actual benefit over write() besides being fluent. What we plan to do should be compatible almost all cases, and we will take care to not do substantial harm to existing implementations, and certainly we will go through a behavior-CSR. The sole change actually planned here is that append(csq) will not forcefully forward to write(str) anymore, as the existing impl spec wording actually makes the append(csq) methods *effectively useless*. The planned change of Javadoc wording will allow subclasses to opt for NOT forward to write(), in our case for the benefit of efficiency. The planned change of the default impl will append(1-arg) become a shorthand for append(3-arg) instead an alias for write(). Unless a corpus analysis proofs us wrong, this should not break most implementations.
19-05-2025

> Implementation behavior is slightly changed w.r.t to self-calls to non-private methods. ... The JavaDocs of these methods can be simplified thanks to the reduced number of self-calls. While this changes the internal behavior slightly, it is finally clarifying that these JavaDoc sections were originally meant as explanatory notes what *the default code* does, but not as as mandatory specs what *subclasses* have to do. These sounds like incompatible changes to me. The Writer (and related) classes were introduced in JDK 1.1 and there are an unknown number of subclasses out in the wild that depend on the exact behavior of the implementations in this class. Changing the behavior of these methods could break such code. The fact that the behavior of the method implementations isn't specified doesn't give us license to change their behavior arbitrarily, even if the results still conform to the specs. I'd prefer to see changes that don't result in any externally visible behavior changes.
19-05-2025