【问题标题】:Most efficient way to make the first character of a String lower case?使字符串的第一个字符小写的最有效方法?
【发布时间】:2011-05-02 10:58:12
【问题描述】:

使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,你可以直接修改它。
  • 好吧,这不是一个答案,因为它在 Java 之外,并且依赖于 ASCII 编码并且知道字符已经是字母。这是一个老前辈的黑客:c[0] |= ' ';
  • 这是一个不同的问题

标签: java string performance optimization


【解决方案1】:

我使用JMH 测试了有希望的方法。完整基准code

测试期间的假设(避免每次都检查极端情况):输入字符串长度始终大于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 是安迪的第二个方法。这也是 Daniel 建议的 Introspector.decapitalize(),但没有两个 if 语句。由于测试假设,首先删除了if。第二个被删除,因为它违反了正确性(即输入"HI" 将返回"HI")。这几乎是最快的了。

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

    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使用反射直接在String中改变char value[]。这是最慢的。

    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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 来自此方法的文档:“这通常意味着将第一个字符从大写转换为小写,但在(不寻常的)特殊情况下,当有多个字符并且第一个和第二个字符是大写的,我们不用管它。”
    • 另外,查看源代码,一旦此方法处理了我在上一条评论中描述的特殊情况,它仅使用我在问题中提到的 char 数组。
    • 正是我需要的。 Introspector.decapitalize("ABC") 仍然是 ABC。 WordUtils.uncapitalize("ABC") 产生“aBC”。只是分享前者是spring如何对bean进行自动命名,所以如果你需要通过bean名称检索ABCService,它不是aBCService,而是ABCService。
    【解决方案3】:

    关于字符串操作,请查看 Jakarta Commons Lang StringUtils

    【讨论】:

    • 更具体地说,使用 StringUtils 的方法 uncapitalize(java.lang.String) 的另一个优点是不必担心代码中的 NullPointerExceptions。
    • 不一定是最高效的,但也许是最清晰的,这很重要。
    • 取决于您要提高效率的资源 - CPU 或程序员时间:)
    【解决方案4】:

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

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

    结果:一些字符串

    【讨论】:

    • 这是一个不错且干净的解决方案,但现在已弃用,我们应该使用 commons-text 的:compile group: 'org.apache.commons', name: 'commons-text', version: '1.2'
    【解决方案5】:

    尽管是面向字符的方法,但我建议使用面向字符串的解决方案。 String.toLowerCase 是特定于语言环境的,所以我会考虑这个问题。根据Character.toLowerCaseString.toLowerCase 更适合小写。 此外,面向 char 的解决方案不完全兼容 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")));
    

    会输出两种不同的结果:

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

      • 其实第一种方式是创建一个临时的String(用于子串),比字符数组要贵。
      • 没有支持数据就无济于事
      【解决方案7】:

      一种非常简短的静态方法来归档您想要的内容:

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

      【讨论】:

        【解决方案8】:

        如果你需要的东西很简单(例如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 字符串操作的原理,请查看this wiki page

        【讨论】:

          【解决方案9】:
          val str = "Hello"
          s"${str.head.toLower}${str.tail}"
          

          结果:

          res4: String = hello
          

          【讨论】:

            【解决方案10】:
            String testString = "SomeInputString";
            String firstLetter = testString.substring(0,1).toLowerCase();
            String restLetters = testString.substring(1);
            String resultString = firstLetter + restLetters;
            

            【讨论】:

              【解决方案11】:

              我今天才遇到这个。试图以最步行的方式自己做。这需要一条线,虽然很长。来了

              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 之后

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-04-25
                • 1970-01-01
                • 2014-03-12
                • 2011-03-14
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多