DATE : 2010/01/13 (Wed)
ある日、大文字が含まれているかもしれない文字列を全て小文字に統一したいという目的からJava SE 6のAPIドキュメントをなんとなく眺めていました。すると、String.toLowerCase()の解説に次のような記述がありました。
注: このメソッドはロケールに依存するため、ロケールが単独で解釈されるような文字列に使用すると、予期せぬ結果を引き起こすことがあります。例として、プログラミング言語識別子、プロトコルキー、HTML タグがあります。たとえば、トルコ語ロケールの "TITLE".toLowerCase() は "t?tle" を返します。ここで、「?」は点のないラテン小文字の I の文字です。ロケールに依存しない文字列の正しい結果を取得するには、toLowerCase(Locale.ENGLISH) を使用します。
つまりトルコ語ロケールの環境下では、小文字変換で「TITLE」は「title」にならず、大文字変換でも「title」は「TITLE」になりません。トルコ語(Wikipedia)に掲載されているトルコ語でのアルファベッドによると、トルコ語では「i」が「İ」(上に点のあるI)、「I」が「ı」(点のないi)に変換されます。
するとロケールによって変換結果が異なっては困る処理でString.toLowerCase()およびString.toUpperCase()を使用すると、ロケールによっては正しく動作しないという現象が発生します。この問題を解決するには、APIドキュメントにもあるとおり、引数にLocale.ENGLISHを指定して、変換対象のロケールを指定できるString.toLowerCase(Locale)やString.toUpperCase(Locale)を使用します。
それでは大文字・小文字を区別せずに比較するString.equalsIgnoreCase(String)はどうなのでしょうか。結論から言えば、String.equalsIgnoreCase(String)はどのロケールであっても結果が同じとなります。
String.equalsIgnoreCase(String)のAPIドキュメントには以下のような記述があります。
この String とほかの String を比較します。大文字と小文字は区別されません。長さが同じで、2 つの文字列内の対応する文字が大文字と小文字の区別なしで等しい場合、2 つの文字列は大文字と小文字の区別なしで等しいと見なされます。
次のどれかに該当する場合に、c1 と c2 という 2 つの文字は大文字小文字の区別なしで等しいと見なされます。
- 2 つの文字が等しい (== 演算子による比較)
- Character.toUpperCase(char) メソッドをそれぞれの文字に適用すると同じ結果になる
- Character.toLowerCase(char) メソッドをそれぞれの文字に適用すると同じ結果になる
String.equalsIgnoreCase(String)で用いられているのは、String.toLowerCase(), String.toUpperCase()ではなくCharacter.toLowerCase(char), Character.toUpperCase(char)です。
Character.toLowerCase(char)のAPIドキュメントには以下のように書かれています。
UnicodeData ファイル内のケースマッピング情報を使用して、文字引数を小文字に変換します。
(中略)
通常、String.toLowerCase() は、文字を小文字にマップするときに使用する必要があります。String ケースマッピングメソッドは、Character ケースマッピングメソッドと比べて複数の利点があります。String ケースマッピングメソッドは、ロケール依存のマッピング、コンテキスト依存のマッピング、および 1:M 文字マッピングを実行できるのに対し、Character ケースマッピングメソッドはそのようなマッピングを実行できません。
ここで重要なのは「通常、String.toLowerCase() は」で始まる部分です。ここにString.toLowerCase()とCharacter.toLowerCase(char)の違いが明記されています。
つまりCharacter.toLowerCase(char), Character.toUpperCase(char)はロケールに依存せず、どのようなロケールでも同じ結果となります。そのためString.equalsIgnoreCase(String)も同様にロケールに依存せず、どのようなロケールでも同じ結果となると言えます。
初めはメソッド名が同じなので、String.toLowerCase(), String.toUpperCase()とCharacter.toLowerCase(char), Character.toUpperCase(char)とが同じと勘違いしてしまいました。
(;^ω^)クラス名やメソッド名が似ているからと言って処理が同じだと判断せずに、ドキュメントはきちんと読むべきでした。