Greetings
Going through the accumulated material, java.lang.String
I decided to make a small selection of examples from effective (and not so) use.
Any line conversion spawns a new line
This is one of the main myths about the lines. In fact, this is not always the case. Suppose we have a string containing only lowercase letters:
var str = "str";
Now this code
jshell> var str = "str";
jshell> System.out.println(str.toLowerCase() == str);
will output
true
In other words, here the call toLowerCase()
returned the line on which it was called. And although this behavior is not described in the documentation, the code StringLatin1.toLowerCase()
leaves no doubt (here and below is the code from https://hg.openjdk.java.net/jdk/jdk/ ):
public static String toLowerCase(String str, byte[] value, Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
int first;
final int len = value.length;
for (first = 0 ; first < len; first++) {
int cp = value[first] & 0xff;
if (cp != CharacterDataLatin1.instance.toLowerCase(cp)) {
break;
}
}
if (first == len)
return str;
}
: , . , , , String.trim()
String.strip()
:
public String trim() {
String ret = isLatin1() ? StringLatin1.trim(value)
: StringUTF16.trim(value);
return ret == null ? this : ret;
}
public String strip() {
String ret = isLatin1() ? StringLatin1.strip(value)
: StringUTF16.strip(value);
return ret == null ? this : ret;
}
:
boolean isUpperCase = name.toUpperCase().equals(name);
- StringUtils
, ( ""). / /, , name.toUpperCase()
name
, ?
boolean isUpperCase = name.toUpperCase() == name;
, , String.toUpperCase()
. ( , ) o.a.c.l.StringUtils.isAllUpperCase()
.
boolean eq = aString.toUpperCase().equals(anotherString);
boolean eq = aString.equalsIgnoreCase(anotherString);
, "" , "".
String.toLowerCase()
String.toLowerCase()
/ String.toUpperCase()
, . :
boolean isEmpty = someStr.toLowerCase().isEmpty();
, . , / . , isEmpty()
true
. false
, . . 1 , .
, :
boolean isEmpty = someStr.isEmpty();
. String.isEmpty()
:
public boolean isEmpty() {
return value.length == 0;
}
int len = someStr.toLowerCase().length();
int len = someStr.length();
, ?
String s = "!";
String s = "!";
, , . . — . , toLowerCase()
/ toUpperCase()
, . , . , :
@Test
void toLowerCase() {
String str = "\u00cc";
assert str.length() == 1;
String strLowerCase = str.toLowerCase(new Locale("lt"));
assert strLowerCase.length() == 3;
}
, : " ?" 1 , ( — 6 (!) ). :
public String toLowerCase(Locale locale) {
}
:
public static String toLowerCase(String str, byte[] value, Locale locale) {
String lang = locale.getLanguage();
if (lang == "tr" || lang == "az" || lang == "lt") {
return toLowerCaseEx(str, value, first, locale, true);
}
}
, , :)
1 — String.substring(n, n+1)
— , , , 1. :
boolean startsWithUnderline = message.substring(0, 1).equals("_");
boolean startsWithUnderline = message.charAt(0) == '_';
, . :
String s = "xxx" + name.substring(n, n + 1);
String s = "xxx" + name.charAt(n);
, . . . , .
— :
boolean startsWithUrl = content.substring(index, index + 4).equals("url(");
boolean startsWithUrl = content.startsWith("url(", index);
. , ( ):
private String findPerClause(String str) {
str = str.substring(str.indexOf('(') + 1);
str = str.substring(0, str.length() - 1);
return str;
}
, , :
( , )
-->
,
, , :
private String findPerClause(String str) {
int beginIndex = str.indexOf('(') + 1;
int endIndex = str.length() - 1;
return str.substring(beginIndex, endIndex);
}
, :
int idx = path.substring(2).indexOf('/');
, String.indexOf(int ch, int fromIndex)
, :
int idx = path.indexOf('/', 2);
. , '/'
2, . . :
int idx = name.indexOf('/', 2);
if (pos != -1) {
idx -= 2;
}
, .
JDK. ,
someStr.substring(n, n);
, n
:
public String substring(int beginIndex, int endIndex) {
int length = length();
checkBoundsBeginEnd(beginIndex, endIndex, length);
int subLen = endIndex - beginIndex;
if (beginIndex == 0 && endIndex == length) {
return this;
}
return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
: StringUTF16.newString(value, beginIndex, subLen);
}
public static String newString(byte[] val, int index, int len) {
return new String(Arrays.copyOfRange(val, index, index + len), LATIN1);
}
beginIndex
endIndex
subLen
0, StringLatin1.newString()
. , :
public static String newString(byte[] val, int index, int len) {
if (len == 0) {
return "";
}
return new String(Arrays.copyOfRange(val, index, index + len), LATIN1);
}
StringLatin1.stripLeading() / stripTrailing()
StringUTF16
. .
, :
public static String stripLeading(byte[] value) {
int left = indexOfNonWhitespace(value);
if (left == value.length) {
return "";
}
return (left != 0) ? newString(value, left, value.length - left) : null;
}
value.length == 0
. left == value.length
newString
,
public static String stripLeading(byte[] value) {
int left = indexOfNonWhitespace(value);
return (left != 0) ? newString(value, left, value.length - left) : null;
}
null
! String.stripLeading()
, this
, . , . :
boolean b= new String("").stripLeading() == "";
boolean b= new String("").stripLeading() == "";
, ?
!
, :
@Warmup(iterations = 10, time = 1)
@Measurement(iterations = 10, time = 1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 3, jvmArgsAppend = {"-Xms4g", "-Xmx4g", "-XX:+UseParallelGC"})
public class SubstringBenchmark {
private static final String str = "Tolstoy";
@Benchmark
public String substring() {
return str.substring(1, 1);
}
}
:
Mode Score Error Units
substring avgt 5.8 ± 0.066 ns/op
substring:·gc.alloc.rate avgt 4325.9 ± 47.259 MB/sec
substring:·gc.alloc.rate.norm avgt 40.0 ± 0.001 B/op
substring:·gc.churn.G1_Eden_Space avgt 4338.8 ± 86.555 MB/sec
substring:·gc.churn.G1_Eden_Space.norm avgt 40.1 ± 0.647 B/op
substring:·gc.churn.G1_Survivor_Space avgt 0.0 ± 0.003 MB/sec
substring:·gc.churn.G1_Survivor_Space.norm avgt ≈ 10⁻⁴ B/op
substring:·gc.count avgt 557.0 counts
substring:·gc.time avgt 387.0 ms
substring avgt 2.4 ± 0.172 ns/op
substring:·gc.alloc.rate avgt 0.0 ± 0.001 MB/sec
substring:·gc.alloc.rate.norm avgt ≈ 10⁻⁵ B/op
substring:·gc.count avgt ≈ 0 counts
, String.substring(n, n)
, .
, , , , . , AnnotationMetadataReadingVisitor-:
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValAsStr) {
String annotatedElement = "class '" + getClassName() + "'";
for (AnnotationAttributes raw : attributes) {
for (Map.Entry<String, Object> entry : convertClassValues(
"class '" + getClassName() + "'", classLoader, raw, classValAsStr).entrySet()) {
allAttributes.add(entry.getKey(), entry.getValue());
}
}
return allAttributes;
}
The expression "class '" + getClassName() + "'"
will be the same and we don’t really want to create the same line in a double loop, so it’s better to create it 1 time outside the loop. Earlier, catching such examples was a matter of chance: I found this one having successfully failed inside the source while debugging my application. Now thanks to IDEA-230889 this can be automated. Of course, it is far from always the creation of a new line in a loop, regardless of the passage, but even in these cases, we can distinguish those in which there is some enduring constant part:
public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
String id = beanName;
int counter = -1;
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
id = beanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
}
return id;
}
Here the prefix is beanName + GENERATED_BEAN_NAME_SEPARATOR
always the same, therefore it can be brought out.
That's all, write your examples in the comments - we will cover it.