diff --git a/src/java.base/share/classes/java/io/Reader.java b/src/java.base/share/classes/java/io/Reader.java index af5ac9f68ec7c..1967fa6576f12 100644 --- a/src/java.base/share/classes/java/io/Reader.java +++ b/src/java.base/share/classes/java/io/Reader.java @@ -27,6 +27,7 @@ import java.nio.CharBuffer; import java.nio.ReadOnlyBufferException; +import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -397,16 +398,6 @@ public int read(char[] cbuf) throws IOException { */ public abstract int read(char[] cbuf, int off, int len) throws IOException; - private String readAllCharsAsString() throws IOException { - StringBuilder result = new StringBuilder(); - char[] cbuf = new char[TRANSFER_BUFFER_SIZE]; - int nread; - while ((nread = read(cbuf, 0, cbuf.length)) != -1) { - result.append(cbuf, 0, nread); - } - return result.toString(); - } - /** * Reads all remaining characters as lines of text. This method blocks until * all remaining characters have been read and end of stream is detected, @@ -457,7 +448,64 @@ private String readAllCharsAsString() throws IOException { * @since 25 */ public List readAllLines() throws IOException { - return readAllCharsAsString().lines().toList(); + char[] cb = new char[TRANSFER_BUFFER_SIZE]; + int pos = 0; + List lines = new ArrayList(); + + StringBuilder sb = new StringBuilder(82); + int n = read(cb, 0, cb.length); + boolean eos = (n == -1); + while (!eos) { + boolean eol = false; + boolean stringAdded = false; + while (!eol) { + // Find the next line terminator. If none is found, + // "term" will equal "n". + int term = pos; + while (term < n) { + char c = cb[term]; + if (c == '\r' || c == '\n') + break; + term++; + } + + // Terminator found so at EOL. + if (term < n) + eol = true; + + if (term == pos) { + // Current position is terminator so skip it. + pos++; + } else { // term > pos + if (eol && sb.length() == 0) { + // Create and add a string to avoid the StringBuilder. + lines.add(new String(cb, pos, term - pos)); + stringAdded = true; + } else + sb.append(cb, pos, term - pos); + pos = term + 1; + } + + if (pos >= n) { + // Buffer content consumed so reload it. + if ((n = read(cb, 0, cb.length)) < 0) { + eos = eol = true; + break; + } + pos = 0; + } + } + + if (!stringAdded) { + // Derive a string and add it to the list. + lines.add(sb.toString()); + sb.setLength(0); + } + + eol = false; + } + + return lines; } /** @@ -499,7 +547,13 @@ public List readAllLines() throws IOException { * @since 25 */ public String readAllAsString() throws IOException { - return readAllCharsAsString(); + StringBuilder result = new StringBuilder(); + char[] cbuf = new char[TRANSFER_BUFFER_SIZE]; + int nread; + while ((nread = read(cbuf, 0, cbuf.length)) != -1) { + result.append(cbuf, 0, nread); + } + return result.toString(); } /** Maximum skip-buffer size */ diff --git a/test/micro/org/openjdk/bench/java/io/ReaderReadAllLines.java b/test/micro/org/openjdk/bench/java/io/ReaderReadAllLines.java new file mode 100644 index 0000000000000..2c9d8113cce6f --- /dev/null +++ b/test/micro/org/openjdk/bench/java/io/ReaderReadAllLines.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.io; + +import java.io.CharArrayReader; +import java.io.IOException; +import java.io.Reader; +import java.util.List; +import java.util.Random; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +@State(Scope.Benchmark) +public class ReaderReadAllLines { + + private char[] chars = null; + + @Setup + public void setup() throws IOException { + final int len = 128_000; + chars = new char[len]; + Random rnd = new Random(System.nanoTime()); + int off = 0; + while (off < len) { + int lineLen = 40 + rnd.nextInt(40); + if (lineLen > len - off) { + off = len; + } else { + chars[off + lineLen] = '\n'; + off += lineLen; + } + } + } + + @Benchmark + public List readAllLines() throws IOException { + List lines; + try (Reader reader = new CharArrayReader(chars);) { + lines = reader.readAllLines(); + } + return lines; + } +}