文字列の最初の文字を小文字にする最も効率的な方法は?


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

これを達成するための素晴らしい方法は他にもたくさんあると思います。何がお勧めですか?


最善の方法は、可能であれば要件を変更することです。Stringの代わりにStringBuilderを受け入れます。これを直接変更できます。
Mark Peters、

まあ、これはJavaの外にあり、ASCIIエンコーディングに依存し、文字がすでにアルファベットであることを知っているため、これは答えではありません。それは、古いタイマーのハックです:c[0] |= ' ';
マイクDunlavey


それは別の質問です
Andy

回答:


123

JMHを使用して有望なアプローチをテストしました。完全なベンチマークコード

テスト中の仮定(毎回コーナーケースをチェックしないようにするため):入力文字列の長さが常に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秒あたりの操作数であり、多いほど優れています。

テスト

  1. test1 アンディとヒリンクの最初のアプローチでした。

    string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
  2. test2アンディの2番目のアプローチでした。それもIntrospector.decapitalize()ダニエルによって提案されましたが、2つのifステートメントはありません。最初ifはテストの仮定のために削除されました。2つ目は正しさに違反していたため削除されました(つまり、入力"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] |= ' 'マイクのコメントから同じパフォーマンスを与えた。

    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. test52つのsubstring()呼び出しを使用しました。

    string = string.substring(0, 1).toLowerCase() + string.substring(1);
  6. test6リフレクションを使用して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


95

サードパーティのライブラリを使用したくない場合は、素晴らしい代替案を見つけました。

import java.beans.Introspector;

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

14
このメソッドのドキュメントから:「これは通常、最初の文字を大文字から小文字に変換することを意味しますが、(異常な)特殊なケースでは、複数の文字があり、最初の文字と2番目の文字の両方が大文字である場合、そのままにします。それだけで。」
アンディ

1
また、ソースを見ると、このメソッドが前のコメントで説明した特殊なケースを処理すると、質問で述べたようにchar配列を使用するだけです。
アンディ

2
まさに私が必要としたもの。Introspector.decapitalize( "ABC")はABCのままです。WordUtils.uncapitalize( "ABC")は "aBC"を生成します。前者は、SpringがBeanの自動命名を行う方法であることを共有するだけなので、ABCServiceをBean名で取得する必要がある場合、それはaBCServiceではなく、ABCServiceです。
村人

21

文字列操作に関しては、Jakarta Commons Lang StringUtilsをご覧ください


8
より具体的には、StringUtilsを使用するメソッドuncapitalize(java.lang.String)には、コード内のNullPointerExceptionsを心配する必要がないという追加の利点があります。
ヘキシウム

3
必ずしも最も効率的であるとは限りませんが、おそらく最も明確で、これは非常に重要です。
David Gelhar、

2
どのリソースをより効率的にするかに依存します
-CPU

15

Apache Commonsを使用したい場合は、以下を実行できます。

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

結果:someString


3
それはきれいなソリューションですが、これは廃止され、我々はコモンズ・テキストの使用する必要があります:compile group: 'org.apache.commons', name: 'commons-text', version: '1.2'
DK7

10

char指向のアプローチにもかかわらず、文字列指向のソリューションを提案します。 String.toLowerCaseはロケール固有なので、この問題を考慮に入れます。Character.toLowerCaseにString.toLowerCaseよると小文字を優先します。また、Character.toLowerCase は補助文字を処理できないため、char指向のソリューションは完全なUnicode対応ではありません。

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

2つの異なる結果が出力されます。


7

Javaの文字列は不変なので、どちらの方法でも新しい文字列が作成されます。

最初の例は、一時的な文字配列ではなく、新しい文字列を作成するだけでよいので、おそらく少し効率的です。


1
実際、最初の方法では、一時的な文字列(部分文字列用)を作成します。これは、文字配列よりもコストがかかります。
Hot Licks 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の後


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.