classlib: improve performance of several String methods

This commit is contained in:
Alexey Andreev 2023-06-07 11:22:01 +02:00
parent cb67595f5b
commit 9dd9fc3a8a

View File

@ -15,7 +15,6 @@
*/ */
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import java.util.Iterator;
import java.util.Locale; import java.util.Locale;
import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.classlib.java.io.TSerializable; import org.teavm.classlib.java.io.TSerializable;
@ -33,12 +32,14 @@ import org.teavm.dependency.PluggableDependency;
import org.teavm.interop.NoSideEffects; import org.teavm.interop.NoSideEffects;
public class TString extends TObject implements TSerializable, TComparable<TString>, TCharSequence { public class TString extends TObject implements TSerializable, TComparable<TString>, TCharSequence {
private static final char[] EMPTY_CHARS = new char[0];
private static final TString EMPTY = new TString();
public static final TComparator<TString> CASE_INSENSITIVE_ORDER = (o1, o2) -> o1.compareToIgnoreCase(o2); public static final TComparator<TString> CASE_INSENSITIVE_ORDER = (o1, o2) -> o1.compareToIgnoreCase(o2);
private char[] characters; private char[] characters;
private transient int hashCode; private transient int hashCode;
public TString() { public TString() {
this.characters = new char[0]; this.characters = EMPTY_CHARS;
} }
public TString(TString other) { public TString(TString other) {
@ -59,6 +60,12 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
} }
} }
static TString fromArray(char[] characters) {
var s = new TString();
s.characters = characters;
return s;
}
public TString(byte[] bytes, int offset, int length, TString charsetName) throws TUnsupportedEncodingException { public TString(byte[] bytes, int offset, int length, TString charsetName) throws TUnsupportedEncodingException {
this(bytes, offset, length, TCharset.forName(charsetName.toString())); this(bytes, offset, length, TCharset.forName(charsetName.toString()));
} }
@ -157,7 +164,6 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
} }
public boolean isBlank() { public boolean isBlank() {
for (int i = 0; i < characters.length; i++) { for (int i = 0; i < characters.length; i++) {
if (characters[i] != ' ') { if (characters[i] != ' ') {
return false; return false;
@ -392,6 +398,12 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
if (beginIndex > endIndex) { if (beginIndex > endIndex) {
throw new TIndexOutOfBoundsException(); throw new TIndexOutOfBoundsException();
} }
if (beginIndex == endIndex) {
return EMPTY;
}
if (beginIndex == 0 && endIndex == length()) {
return this;
}
return new TString(characters, beginIndex, endIndex - beginIndex); return new TString(characters, beginIndex, endIndex - beginIndex);
} }
@ -408,7 +420,11 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
if (str.isEmpty()) { if (str.isEmpty()) {
return this; return this;
} }
char[] buffer = new char[length() + str.length()]; if (isEmpty()) {
return str;
}
var buffer = new char[length() + str.length()];
int index = 0; int index = 0;
for (int i = 0; i < length(); ++i) { for (int i = 0; i < length(); ++i) {
buffer[index++] = charAt(i); buffer[index++] = charAt(i);
@ -416,18 +432,18 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
for (int i = 0; i < str.length(); ++i) { for (int i = 0; i < str.length(); ++i) {
buffer[index++] = str.charAt(i); buffer[index++] = str.charAt(i);
} }
return new TString(buffer); return TString.fromArray(buffer);
} }
public TString replace(char oldChar, char newChar) { public TString replace(char oldChar, char newChar) {
if (oldChar == newChar) { if (oldChar == newChar) {
return this; return this;
} }
char[] buffer = new char[length()]; var buffer = new char[length()];
for (int i = 0; i < length(); ++i) { for (int i = 0; i < length(); ++i) {
buffer[i] = charAt(i) == oldChar ? newChar : charAt(i); buffer[i] = charAt(i) == oldChar ? newChar : charAt(i);
} }
return new TString(buffer); return TString.fromArray(buffer);
} }
public boolean contains(TCharSequence s) { public boolean contains(TCharSequence s) {
@ -445,7 +461,7 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
} }
public String replace(TCharSequence target, TCharSequence replacement) { public String replace(TCharSequence target, TCharSequence replacement) {
StringBuilder sb = new StringBuilder(); var sb = new StringBuilder();
int sz = length() - target.length(); int sz = length() - target.length();
int i = 0; int i = 0;
outer: outer:
@ -706,19 +722,19 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
return new TFormatter(l).format(format, args).toString(); return new TFormatter(l).format(format, args).toString();
} }
public static String join(CharSequence delimiter, CharSequence... elements) { public static TString join(CharSequence delimiter, CharSequence... elements) {
if (elements.length == 0) { if (elements.length == 0) {
return ""; return EMPTY;
} }
int resultLength = 0; int resultLength = 0;
for (CharSequence element : elements) { for (var element : elements) {
resultLength += element.length(); resultLength += element.length();
} }
resultLength += (elements.length - 1) * delimiter.length(); resultLength += (elements.length - 1) * delimiter.length();
char[] chars = new char[resultLength]; var chars = new char[resultLength];
int index = 0; int index = 0;
CharSequence firstElement = elements[0]; var firstElement = elements[0];
for (int i = 0; i < firstElement.length(); ++i) { for (int i = 0; i < firstElement.length(); ++i) {
chars[index++] = firstElement.charAt(i); chars[index++] = firstElement.charAt(i);
} }
@ -726,22 +742,22 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
for (int j = 0; j < delimiter.length(); ++j) { for (int j = 0; j < delimiter.length(); ++j) {
chars[index++] = delimiter.charAt(j); chars[index++] = delimiter.charAt(j);
} }
CharSequence element = elements[i]; var element = elements[i];
for (int j = 0; j < element.length(); ++j) { for (int j = 0; j < element.length(); ++j) {
chars[index++] = element.charAt(j); chars[index++] = element.charAt(j);
} }
} }
return new String(chars); return TString.fromArray(chars);
} }
public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements) { public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements) {
Iterator<? extends CharSequence> iter = elements.iterator(); var iter = elements.iterator();
if (!iter.hasNext()) { if (!iter.hasNext()) {
return ""; return "";
} }
StringBuilder sb = new StringBuilder(); var sb = new StringBuilder();
sb.append(iter.next()); sb.append(iter.next());
while (iter.hasNext()) { while (iter.hasNext()) {
sb.append(delimiter); sb.append(delimiter);
@ -750,20 +766,22 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
return sb.toString(); return sb.toString();
} }
public String repeat(int count) { public TString repeat(int count) {
if (count < 0) { if (count < 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
if (count == 1) { if (count == 1) {
return this.toString(); return this;
} }
if (characters.length == 0 || count == 0) { if (characters.length == 0 || count == 0) {
return ""; return EMPTY;
} }
TStringBuilder builder = new TStringBuilder(characters.length * count); var chars = new char[characters.length * count];
var j = 0;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
builder.append(this.toString()); getChars(0, length(), chars, j);
j += length();
} }
return builder.toString(); return TString.fromArray(chars);
} }
} }