使String的第一个字符小写的最有效方法?


97

使String小写的第一个字符最有效的方法是什么?

我可以想到许多方法来做到这一点:

使用charAt()substring()

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

或使用char数组

 String input  = "SomeInputString";
 char c[]      = input.toCharArray();
 c[0]          = Character.toLowerCase(c[0]);
 String output = new String(c);

我相信还有许多其他好的方法可以实现这一目标。您有什么推荐的吗?


最好的方法是尽可能更改您的要求。接受StringBuilder而不是String,您可以直接对其进行修改。
马克·彼得斯2010年

嗯,这不是答案,因为它在Java之外,并且依赖于ASCII编码并且知道该字符已经是字母。这是老朋友的骇客:c[0] |= ' ';
Mike Dunlavey


这是一个不同的问题
Andy

Answers:


123

我使用JMH测试了有前途的方法。完整的基准代码

测试期间的假设(以避免每次都检查极端情况):输入的String长度始终大于1。

结果

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

分数是每秒的操作数,越高越好。

测验

  1. test1 首先是Andy和Hllink的方法:

    string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
  2. test2是第二安迪的方法。Introspector.decapitalize()丹尼尔(Daniel )也提出了建议,但没有两个if陈述。if由于测试假设,第一个被删除。第二个被删除,因为它违反了正确性(即输入"HI"将返回"HI")。这几乎是最快的。

    char c[] = string.toCharArray();
    c[0] = Character.toLowerCase(c[0]);
    string = new String(c);
  3. test3是对的修改test2,但不是,而是Character.toLowerCase()添加了32,当且仅当字符串为ASCII时,该字体才能正常工作。这是最快的。c[0] |= ' '来自Mike的评论给出了相同的表现。

    char c[] = string.toCharArray();
    c[0] += 32;
    string = new String(c);
  4. test4用过的StringBuilder

    StringBuilder sb = new StringBuilder(string);
    sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
    string = sb.toString();
  5. test5用了两个substring()电话。

    string = string.substring(0, 1).toLowerCase() + string.substring(1);
  6. test6使用反射char value[]直接在String中进行更改。这是最慢的。

    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();
    }

结论

如果字符串长度始终大于0,请使用test2

如果没有,我们必须检查一些特殊情况:

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);
}

如果您确定文本将始终为ASCII并且由于在瓶颈中发现了此代码而正在寻求极佳的性能,请使用test3


95

如果您不想使用第三方库,我遇到了一个不错的选择:

import java.beans.Introspector;

Assert.assertEquals("someInputString", Introspector.decapitalize("SomeInputString"));

14
在此方法的文档中:“这通常意味着将第一个字符从大写转换为小写,但是在(不寻常的)特殊情况下,当有多个字符并且第一个和第二个字符均为大写时,我们离开一个人。”
安迪

1
同样,从源头上看,一旦此方法处理了我在上一条注释中描述的特殊情况,它仅使用我在问题中提到的char数组。
安迪

2
正是我所需要的。Introspector.decapitalize(“ ABC”)仍为ABC。WordUtils.uncapitalize(“ ABC”)产生“ aBC”。只是分享前者是spring对bean进行自动命名的方式,因此,如果需要通过bean名称检索ABCService,则不是aBCService,而是ABCService。
村民,


15

如果要使用Apache Commons,可以执行以下操作:

import org.apache.commons.lang3.text.WordUtils;
[...] 
String s = "SomeString"; 
String firstLower = WordUtils.uncapitalize(s);

结果:someString


3
这是一个不错的解决方案,但是现在不建议使用,我们应该使用commons-text:compile group: 'org.apache.commons', name: 'commons-text', version: '1.2'
dk7

10

尽管采用了面向字符的方法,但我还是建议使用面向字符串的解决方案。 String.toLowerCase是特定于语言环境的,因此我将考虑此问题。String.toLowerCase根据Character.toLowerCase首选小写。同样,面向字符的解决方案也不完全兼容Unicode,因为Character.toLowerCase 无法处理补充字符。

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;
    }

更新: 作为示例,让我们I用土耳其语和德语将小写的语言环境设置多么重要:

System.out.println(uncapitalize("I", new Locale("TR","tr")));
System.out.println(uncapitalize("I", new Locale("DE","de")));

将输出两个不同的结果:

一世

一世


7

Java中的字符串是不可变的,因此无论哪种方式都将创建新的字符串。

您的第一个示例可能会稍微更高效,因为它仅需要创建一个新字符串,而不需要一个临时字符数组。


1
实际上,第一种方法创建一个临时的String(用于子字符串),它比字符数组昂贵。
热门点击2012年

1
没有支持数据
无助

3

一种非常简短的静态方法来存档所需的内容:

public static String decapitalizeString(String string) {
    return string == null || string.isEmpty() ? "" : Character.toLowerCase(string.charAt(0)) + string.substring(1);
}

2

如果您需要的非常简单(例如,java类名称,没有语言环境),则还可以使用Google Guava库中的CaseFormat类。

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

或者,您可以准备并重用转换器对象,这样可能会更有效率。

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

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

为了更好地理解Google Guava字符串操作的原理,请查看此Wiki页面


1
String testString = "SomeInputString";
String firstLetter = testString.substring(0,1).toLowerCase();
String restLetters = testString.substring(1);
String resultString = firstLetter + restLetters;

1

我只是今天才遇到这个问题。试图以最步行的方式自己做。那花了一条线,很久。开始

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);

给出:

在str之前= TaxoRanks

在str之后= taxoRanks


1
val str = "Hello"
s"${str.head.toLower}${str.tail}"

结果:

res4: String = hello
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.