前言:
发布的LTS JDK 11 release了解到对String字符串有些Api更改。
repeat()方法:此方法允许将字符串与其自身连接给定次数。
var string = "foo bar ";
var result = string.repeat(2); // foo bar foo bar
@Test
void shouldRepeatZeroTimes() {
var string = "foo";
var result = string.repeat(0);
System.out.print(result.equals(""));
}
同样适用于重复空字符串:
@Test
void shouldRepeatEmpty() {
var string = "";
var result = string.repeat(Integer.MAX_VALUE);
assertThat(result).isEqualTo("");
}
其源码查看不仅仅依赖于下面的一个StringBuilder,实际实施更具资源效率:
public String repeat(int count) {
if (count < 0) {
throw new IllegalArgumentException("count is negative: " + count);
}
if (count == 1) {
return this;
}
final int len = value.length;
if (len == 0 || count == 0) { //空字符串的兼容
return "";
}
if (len == 1) {
final byte[] single = new byte[count];
Arrays.fill(single, value[0]);
return new String(single, coder);
}
if (Integer.MAX_VALUE / count < len) {
throw new OutOfMemoryError("Repeating " + len + " bytes String " + count +
" times will produce a String exceeding maximum size.");
}
final int limit = len * count;
final byte[] multiple = new byte[limit];
System.arraycopy(value, 0, multiple, 0, len);
int copied = len;
for (; copied < limit - copied; copied <<= 1) {
System.arraycopy(multiple, 0, multiple, copied, copied);
}
System.arraycopy(multiple, 0, multiple, copied, limit - copied);
return new String(multiple, coder);
}
我们可以检查一个字符串实例是否为空,或者是否只包含空格而不用使用StringUitls这种外部帮助类:
var result = " ".isBlank(); // true
去掉每个字符串中的所有空白。
System.out.print(" ".strip().isBlank());//true
或者控制字符串前后进行空白的去除:
System.out.print(" foo ".stripLeading().equals("foo "));
System.out.print(" foo ".stripTrailing()).equals(" foo");
使用此新方法,我们可以轻松地将字符串实例拆分为单独行的Stream
"foo\nbar".lines().forEach(System.out::println);
// foo
// bar
其源码实现了专门的拆分器,拆分字符串并将其转换为流,这样可以保持懒惰:
private final static class LinesSpliterator implements Spliterator {
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 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;
}
}