Most efficient way to make the first character of a String lower case?
Solution 1
I tested the promising approaches using JMH. Full benchmark code.
Assumption during the tests (to avoid checking the corner cases every time): the input String length is always greater than 1.
Results
Benchmark Mode Cnt Score Error Units
MyBenchmark.test1 thrpt 20 10463220.493 ± 288805.068 ops/s
MyBenchmark.test2 thrpt 20 14730158.709 ± 530444.444 ops/s
MyBenchmark.test3 thrpt 20 16079551.751 ± 56884.357 ops/s
MyBenchmark.test4 thrpt 20 9762578.446 ± 584316.582 ops/s
MyBenchmark.test5 thrpt 20 6093216.066 ± 180062.872 ops/s
MyBenchmark.test6 thrpt 20 2104102.578 ± 18705.805 ops/s
The score are operations per second, the more the better.
Tests
-
test1
was first Andy's and Hllink's approach:string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
-
test2
was second Andy's approach. It is alsoIntrospector.decapitalize()
suggested by Daniel, but without twoif
statements. Firstif
was removed because of the testing assumption. The second one was removed, because it was violating correctness (i.e. input"HI"
would return"HI"
). This was almost the fastest.char c[] = string.toCharArray(); c[0] = Character.toLowerCase(c[0]); string = new String(c);
-
test3
was a modification oftest2
, but instead ofCharacter.toLowerCase()
, I was adding 32, which works correctly if and only if the string is in ASCII. This was the fastest.c[0] |= ' '
from Mike's comment gave the same performance.char c[] = string.toCharArray(); c[0] += 32; string = new String(c);
-
test4
usedStringBuilder
.StringBuilder sb = new StringBuilder(string); sb.setCharAt(0, Character.toLowerCase(sb.charAt(0))); string = sb.toString();
-
test5
used twosubstring()
calls.string = string.substring(0, 1).toLowerCase() + string.substring(1);
-
test6
uses reflection to changechar value[]
directly in String. This was the slowest.try { Field field = String.class.getDeclaredField("value"); field.setAccessible(true); char[] value = (char[]) field.get(string); value[0] = Character.toLowerCase(value[0]); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); }
Conclusions
If the String length is always greater than 0, use test2
.
If not, we have to check the corner cases:
public static String decapitalize(String string) {
if (string == null || string.length() == 0) {
return string;
}
char c[] = string.toCharArray();
c[0] = Character.toLowerCase(c[0]);
return new String(c);
}
If you are sure that your text will be always in ASCII and you are looking for extreme performance because you found this code in the bottleneck, use test3
.
Solution 2
I came across a nice alternative if you don't want to use a third-party library:
import java.beans.Introspector;
Assert.assertEquals("someInputString", Introspector.decapitalize("SomeInputString"));
Solution 3
When it comes to string manipulation take a look to Jakarta Commons Lang StringUtils.
Solution 4
If you want to use Apache Commons you can do the following:
import org.apache.commons.lang3.text.WordUtils;
[...]
String s = "SomeString";
String firstLower = WordUtils.uncapitalize(s);
Result: someString
Solution 5
Despite a char oriented approach I would suggest a String oriented solution.
String.toLowerCase is Locale specific, so I would take this issue into account. String.toLowerCase
is to prefer for lower-caseing according to Character.toLowerCase.
Also a char oriented solution is not full unicode compatible, because Character.toLowerCase cannot handle supplementary characters.
public static final String uncapitalize(final String originalStr,
final Locale locale) {
final int splitIndex = 1;
final String result;
if (originalStr.isEmpty()) {
result = originalStr;
} else {
final String first = originalStr.substring(0, splitIndex).toLowerCase(
locale);
final String rest = originalStr.substring(splitIndex);
final StringBuilder uncapStr = new StringBuilder(first).append(rest);
result = uncapStr.toString();
}
return result;
}
UPDATE:
As an example how important the locale setting is let us lowercase I
in turkish and german:
System.out.println(uncapitalize("I", new Locale("TR","tr")));
System.out.println(uncapitalize("I", new Locale("DE","de")));
will output two different results:
ı
i
Related videos on Youtube
Andy
I do software development for Model Based Systems Engineering tools.
Updated on July 08, 2022Comments
-
Andy almost 2 years
What is the most efficient way to make the first character of a
String
lower case?I can think of a number of ways to do this:
Using
charAt()
withsubstring()
String input = "SomeInputString"; String output = Character.toLowerCase(input.charAt(0)) + (input.length() > 1 ? input.substring(1) : "");
Or using a
char
arrayString input = "SomeInputString"; char c[] = input.toCharArray(); c[0] = Character.toLowerCase(c[0]); String output = new String(c);
I am sure there are many other great ways to achieve this. What do you recommend?
-
Mark Peters over 13 yearsThe best way would be to change your requirements if possible. Accept a StringBuilder instead of a String and you can modify it directly.
-
Mike Dunlavey over 13 yearsWell this is not an answer because it's outside of Java, and relies on ASCII encoding and on knowing that the character is already alphabetic. It's an old-timer's hack:
c[0] |= ' ';
-
Raedwald about 10 yearspossible duplicate of Converting to upper and lower case in Java
-
Andy about 10 yearsthat's a different question
-
-
hexium over 13 yearsMore specifically, the method uncapitalize(java.lang.String) Using StringUtils has the added advantage of not having to worry about NullPointerExceptions in your code.
-
David Gelhar over 13 yearsNot necessarily the most efficient, but perhaps the clearest, which counts for a lot.
-
Hot Licks over 11 yearsActually, the first way creates a temporary String (for substring), which is more expensive than the character array.
-
Andy almost 11 yearsFrom the doc for this method: "This normally means converting the first character from upper case to lower case, but in the (unusual) special case when there is more than one character and both the first and second characters are upper case, we leave it alone."
-
Andy almost 11 yearsAlso, looking at the source, once this method handles the special case I described in the previous comment, it merely uses the char array as I had mentioned in my question.
-
Nitsan Wakart almost 9 yearsUnhelpful without supporting data
-
Dan Gravell about 7 yearsDepends what resource you are making more efficient - CPU or programmer time :)
-
dk7 over 6 yearsIt's nice and clean solution, but this is deprecated now, we should use commons-text's:
compile group: 'org.apache.commons', name: 'commons-text', version: '1.2'
-
lorrainebatol almost 5 yearsExactly what I needed. Introspector.decapitalize("ABC") will still be ABC. WordUtils.uncapitalize("ABC") produces "aBC". Just sharing that the former is how spring does its autonaming of beans, so if you need to retrieve by bean name the ABCService, it's not aBCService, but ABCService still.