8200380: String::lines

Reviewed-by: sundar, sherman, rriggs, psandoz
This commit is contained in:
Jim Laskey 2018-05-22 12:04:05 -03:00
parent c3cdecd32b
commit 60de3c902e
4 changed files with 327 additions and 0 deletions

View file

@ -41,6 +41,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.vm.annotation.Stable;
@ -2753,6 +2754,39 @@ public final class String
}
}
/**
* Returns a stream of substrings extracted from this string
* partitioned by line terminators.
* <p>
* Line terminators recognized are line feed
* {@code "\n"} ({@code U+000A}),
* carriage return
* {@code "\r"} ({@code U+000D})
* and a carriage return followed immediately by a line feed
* {@code "\r\n"} ({@code U+000D U+000A}).
* <p>
* The stream returned by this method contains each line of
* this string that is terminated by a line terminator except that
* the last line can either be terminated by a line terminator or the
* end of the string.
* The lines in the stream are in the order in which
* they occur in this string and do not include the line terminators
* partitioning the lines.
*
* @implNote This method provides better performance than
* split("\R") by supplying elements lazily and
* by faster search of new line terminators.
*
* @return the stream of strings extracted from this string
* partitioned by line terminators
*
* @since 11
*/
public Stream<String> lines() {
return isLatin1() ? StringLatin1.lines(value)
: StringUTF16.lines(value);
}
/**
* This object (which is already a string!) is itself returned.
*

View file

@ -29,8 +29,11 @@ import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.HotSpotIntrinsicCandidate;
import static java.lang.String.LATIN1;
@ -589,6 +592,100 @@ final class StringLatin1 {
return (right != value.length) ? newString(value, 0, right) : null;
}
private final static class LinesSpliterator implements Spliterator<String> {
private byte[] value;
private int index; // current index, modified on advance/split
private final int fence; // one past last index
LinesSpliterator(byte[] value) {
this(value, 0, value.length);
}
LinesSpliterator(byte[] value, int start, int length) {
this.value = value;
this.index = start;
this.fence = start + length;
}
private int indexOfLineSeparator(int start) {
for (int current = start; current < fence; current++) {
byte ch = value[current];
if (ch == '\n' || ch == '\r') {
return current;
}
}
return fence;
}
private int skipLineSeparator(int start) {
if (start < fence) {
if (value[start] == '\r') {
int next = start + 1;
if (next < fence && value[next] == '\n') {
return next + 1;
}
}
return start + 1;
}
return fence;
}
private String next() {
int start = index;
int end = indexOfLineSeparator(start);
index = skipLineSeparator(end);
return newString(value, start, end - start);
}
@Override
public boolean tryAdvance(Consumer<? super String> action) {
if (action == null) {
throw new NullPointerException("tryAdvance action missing");
}
if (index != fence) {
action.accept(next());
return true;
}
return false;
}
@Override
public void forEachRemaining(Consumer<? super String> action) {
if (action == null) {
throw new NullPointerException("forEachRemaining action missing");
}
while (index != fence) {
action.accept(next());
}
}
@Override
public Spliterator<String> trySplit() {
int half = (fence + index) >>> 1;
int mid = skipLineSeparator(indexOfLineSeparator(half));
if (mid < fence) {
int start = index;
index = mid;
return new LinesSpliterator(value, start, mid - start);
}
return null;
}
@Override
public long estimateSize() {
return fence - index + 1;
}
@Override
public int characteristics() {
return Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL;
}
}
static Stream<String> lines(byte[] value) {
return StreamSupport.stream(new LinesSpliterator(value), false);
}
public static void putChar(byte[] val, int index, int c) {
//assert (canEncode(c));
val[index] = (byte)(c);

View file

@ -28,7 +28,10 @@ package java.lang;
import java.util.Arrays;
import java.util.Locale;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.DontInline;
@ -911,6 +914,100 @@ final class StringUTF16 {
return (right != length) ? newString(value, 0, right) : null;
}
private final static class LinesSpliterator implements Spliterator<String> {
private byte[] value;
private int index; // current index, modified on advance/split
private final int fence; // one past last index
LinesSpliterator(byte[] value) {
this(value, 0, value.length >>> 1);
}
LinesSpliterator(byte[] value, int start, int length) {
this.value = value;
this.index = start;
this.fence = start + length;
}
private int indexOfLineSeparator(int start) {
for (int current = start; current < fence; current++) {
char ch = getChar(value, current);
if (ch == '\n' || ch == '\r') {
return current;
}
}
return fence;
}
private int skipLineSeparator(int start) {
if (start < fence) {
if (getChar(value, start) == '\r') {
int next = start + 1;
if (next < fence && getChar(value, next) == '\n') {
return next + 1;
}
}
return start + 1;
}
return fence;
}
private String next() {
int start = index;
int end = indexOfLineSeparator(start);
index = skipLineSeparator(end);
return newString(value, start, end - start);
}
@Override
public boolean tryAdvance(Consumer<? super String> action) {
if (action == null) {
throw new NullPointerException("tryAdvance action missing");
}
if (index != fence) {
action.accept(next());
return true;
}
return false;
}
@Override
public void forEachRemaining(Consumer<? super String> action) {
if (action == null) {
throw new NullPointerException("forEachRemaining action missing");
}
while (index != fence) {
action.accept(next());
}
}
@Override
public Spliterator<String> trySplit() {
int half = (fence + index) >>> 1;
int mid = skipLineSeparator(indexOfLineSeparator(half));
if (mid < fence) {
int start = index;
index = mid;
return new LinesSpliterator(value, start, mid - start);
}
return null;
}
@Override
public long estimateSize() {
return fence - index + 1;
}
@Override
public int characteristics() {
return Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL;
}
}
static Stream<String> lines(byte[] value) {
return StreamSupport.stream(new LinesSpliterator(value), false);
}
private static void putChars(byte[] val, int index, char[] str, int off, int end) {
while (off < end) {
putChar(val, index++, str[off++]);