130

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() with substring()

String input   = "SomeInputString";
String output  = Character.toLowerCase(input.charAt(0)) +
                   (input.length() > 1 ? input.substring(1) : "");

Or using a char array

 String 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?

Andy
  • 5,108
  • 3
  • 26
  • 37
  • The best way would be to change your requirements if possible. Accept a StringBuilder instead of a String and you can modify it directly. – Mark Peters Oct 29 '10 at 19:06
  • Well 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] |= ' ';` – Mike Dunlavey Oct 29 '10 at 19:54
  • possible duplicate of [Converting to upper and lower case in Java](http://stackoverflow.com/questions/2375649/converting-to-upper-and-lower-case-in-java) – Raedwald Mar 01 '14 at 13:00
  • that's a different question – Andy Mar 13 '14 at 05:07

11 Answers11

160

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

  1. test1 was first Andy's and Hllink's approach:

     string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
    
  2. test2 was second Andy's approach. It is also Introspector.decapitalize() suggested by Daniel, but without two if statements. First if 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);
    
  3. test3 was a modification of test2, but instead of Character.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);
    
  4. test4 used StringBuilder.

     StringBuilder sb = new StringBuilder(string);
     sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
     string = sb.toString();
    
  5. test5 used two substring() calls.

     string = string.substring(0, 1).toLowerCase() + string.substring(1);
    
  6. test6 uses reflection to change char 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.

Adam Stelmaszczyk
  • 19,665
  • 4
  • 70
  • 110
103

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"));
Daniel Pacak
  • 1,388
  • 2
  • 13
  • 12
  • 19
    From 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 Jun 04 '13 at 02:45
  • 1
    Also, 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. – Andy Jun 04 '13 at 02:46
  • 4
    Exactly 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. – lorraine batol Jul 31 '19 at 09:47
25

When it comes to string manipulation take a look to Jakarta Commons Lang StringUtils.

Adam Stelmaszczyk
  • 19,665
  • 4
  • 70
  • 110
Carlos Tasada
  • 4,438
  • 1
  • 23
  • 26
  • 9
    More specifically, the method uncapitalize(java.lang.String) Using StringUtils has the added advantage of not having to worry about NullPointerExceptions in your code. – hexium Oct 29 '10 at 14:56
  • 3
    Not necessarily the most efficient, but perhaps the clearest, which counts for a lot. – David Gelhar Oct 29 '10 at 15:02
  • 2
    Depends what resource you are making more efficient - CPU or programmer time :) – Dan Gravell Mar 20 '17 at 16:33
20

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

Sebastian
  • 868
  • 9
  • 13
  • 3
    It'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'` – dk7 Jan 26 '18 at 13:44
10

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

Community
  • 1
  • 1
Michael Konietzka
  • 5,419
  • 2
  • 28
  • 29
7

Strings in Java are immutable, so either way a new string will be created.

Your first example will probably be slightly more efficient because it only needs to create a new string and not a temporary character array.

Alan Geleynse
  • 24,821
  • 5
  • 46
  • 55
4

A very short and simple static method to archive what you want:

public static String decapitalizeString(String string) {
    return string == null || string.isEmpty() ? "" : Character.toLowerCase(string.charAt(0)) + string.substring(1);
}
Adam Stelmaszczyk
  • 19,665
  • 4
  • 70
  • 110
Hllink
  • 918
  • 5
  • 17
3
val str = "Hello"
s"${str.head.toLower}${str.tail}"

Result:

res4: String = hello
Vivek
  • 320
  • 1
  • 5
2

If what you need is very simple (eg. java class names, no locales), you can also use the CaseFormat class in the Google Guava library.

String converted = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, "FooBar");
assertEquals("fooBar", converted);

Or you can prepare and reuse a converter object, which could be more efficient.

Converter<String, String> converter=
    CaseFormat.UPPER_CAMEL.converterTo(CaseFormat.LOWER_CAMEL);

assertEquals("fooBar", converter.convert("FooBar"));

To better understand philosophy of the Google Guava string manipulation, check out this wiki page.

Peter Lamberg
  • 8,151
  • 3
  • 55
  • 69
1
String testString = "SomeInputString";
String firstLetter = testString.substring(0,1).toLowerCase();
String restLetters = testString.substring(1);
String resultString = firstLetter + restLetters;
Bae Cheol Shin
  • 1,498
  • 1
  • 11
  • 10
1

I have come accross this only today. Tried to do it myself in the most pedestrian way. That took one line, tho longish. Here goes

String str = "TaxoRank"; 

System.out.println(" Before str = " + str); 

str = str.replaceFirst(str.substring(0,1), str.substring(0,1).toLowerCase());

System.out.println(" After str = " + str);

Gives:

Before str = TaxoRanks

After str = taxoRanks

user3501758
  • 63
  • 1
  • 7