[???] /
[Java FAQ] / [S008]
S008: 文字列 - String
[S008 Q-01]
文字列の内容の比較はどのように行えば良いのでしょうか?
[S008 A-01]
String#equals() を用います。
"abcdefg"、"わーい" などの定数文字列も含めて、
文字列は全て java.lang.String クラスのインスタンスです。
== や != では、両辺が同一オブジェクトであるかどうかが比較されるのであって、
内容の比較を行なうわけではありません。
例えば、以下のようなコードを考えます。
String str1 = "abcde";
String str2 = "abcdefghijk";
このとき、次の式の結果は false となります。
str1 == str2.substring(0,5)
これは、str2 の最初5文字は "abcde" で、str1と同じ内容ですが、str1とは異なる
実体となっているからです。
String#equals(String another) を用いることで、内容の比較を行なうことができます。
例えば、次の式の結果は true となります。
str1.equals(str2.substring(0,5))
ただし、文字列リテラル (定数文字列のこと) は同一内容の場合に同一オブジェクトとな
ります。これは、文字列リテラルを登録する共用プールがシステムに用意されており、
既に同一内容のリテラルが登録されている場合にはそれを参照するからです。
例えば、以下のコードでisSameObject変数の値は true となります。
String str1 = "abcde";
String str2 = "abcde";
boolean isSameObject = (str1 == str2);
この、文字列リテラルの共用は、複数のクラスにまたがって行なわれるため、
別々のクラスで定義した場合でも、同一内容の文字列リテラルは一つの実体に
なります。
参考記事 [JavaHouse-Brewers:1007],[JavaHouse-Brewers:25559]
[S008 Q-02]
switch で String が使えないのですが?
[S008 A-02]
はい、つかえません。コンパイルエラーとなります。
switch で使えるのは、char, byte, short, int のみです。
switch の条件に使う式は int 型である必要があります。
long なども、int にキャストしなくてはいけません。
String の内容で分岐したい場合には、以下の例のように if-else を用います。
if ("case1".equals(param)) {
// "case1" だった場合の処理
} else if ("case2".equals(param)) {
// "case2" だった場合の処理
:
}
参考記事 [JavaHouse-Brewers:4049]
[S008 Q-03]
Stringに「"」を含めたいのですが?
[S008 A-03]
前に \ をつけてエスケープします。
例えば、「"abcd"」という内容の文字列リテラルは次のように記述することができます。
String string = "\"abcd\"";
参考記事 [JavaHouse-Brewers:7923]
[S008 Q-04]
StringBuffer はどういう時に使用するのでしょうか?
[S008 A-04]
文字列の内容を編集したい場合に用います。
Java では C や C++ のように一度宣言した String オブジェクトの
内容を変えることはできません。文字列の最後に文字を追加したり、
途中に文字を挿入したり、置換を行ったりする場合には StringBuffer
オブジェクトを使います。
単純に文字列を連結するだけであれば、次のように + を用いて行なうこともできます。
String str = "abcd";
str = str + "efgh";
このように記述すると、内部的には以下のコードに相当することを行ないます。
String str = "abcd";
StringBuffer buffer = new StringBuffer(String.valueOf(str));
buffer.append("efgh");
str = buffer.toString();
この書き方をループ内で使用すると非常に効率が悪くなります。
最初からStringBufferを用いて以下の様に記述する方が効率的で
あるといえます。
public String test (Properties prop) {
StringBuffer str = new StringBuffer();
Enumeration enu = prop.values();
while (enu.hasMoreElements()) {
str.append ((String)enu.nextElement()); }
return new String (str); }
参考記事 [JavaHouse-Brewers:6532]
[S008 Q-05]
文字列を編集したいのですが?
[S008 A-05]
StringBufferを用います。
StringBufferの具体的な使い方については、S008-06 を参照してください。
参考記事 [JavaHouse-Brewers:1025]
[S008 Q-06]
StringBuffer の使い方は?
[S008 A-06]
行ないたい文字列操作に応じたメソッドを呼び出します。
末尾に文字列を追加するには、StringBuffer#append() を用います。
引数の型が複数用意されており、以下のようにString以外の値を文字列に変換して追加
することもできます。
StringBuffer buffer = new StringBuffer();
buffer.append("abcdefg");
buffer.append(12345);
buffer.append('$');
buffer.append(-9.8);
buffer.append(true);
途中に文字列を挿入するには、StringBuffer#insert() を用います。
挿入する位置は、先頭を0とする整数で指定します。
例えば、次のコードを実行するとresult変数の値は "AあBいうC" となります。
StringBuffer buffer = new StringBuffer("あいう");
buffer.insert(1, "B");
buffer.insert(4, "C");
buffer.insert(0, "A");
String result = new String(buffer);
これも StringBuffer#append() 同様、引数の型が複数用意されており、
String以外の値を文字列に変換して挿入することができます。
特定の範囲の文字列を置換するには、StringBuffer#replace() を用います。
置換する範囲は、置換対象の先頭文字の位置と、末尾文字の次の文字の位置で指定します。
例えば、次のコードを実行するとresult変数の値は "あkasaたな" となります。
StringBuffer buffer = new StringBuffer("あかさたな");
buffer.replace(1, 3, "kasa");
String result = buffer.toString();
特定の範囲の文字列を削除するには、StringBuffer#delete() を用います。
削除する範囲の指定方法は、置換の場合と同様です。
参考記事 [JavaHouse-Brewers:1025]
[S008 Q-07]
new String("abc") という書き方にはどんな意味があるのですか?
[S008 A-07]
"abc"という内容を持つ、新しい文字列(Stringオブジェクト)を生成します。
Javaでは、S008-01「文字列の内容の比較はどのように行えば良いのでしょうか?」
で説明した通り、同一内容の文字列リテラルは同じ実体となります。
new String("abc")という表記を用いることで、明示的に別な実体を生成することがで
きます。
[S008 Q-08]
文字列のバイト数を取得するにはどうしたら良いのですか?
[S008 A-08]
簡単には得られません。
String#length()によって文字数を得ることができますが、Javaの内部では文字列はUnicodeで扱われているため、
この文字数はバイト数とは異なります。
一般に、ある文字列をバイト列にした場合のバイト数は、どの文字コードで表現するか
(どのエンコーディングを用いるか)によって異なります。したがって、バイト数を知
るためにはエンコーディングを指定する必要があります。
例えば、シフトJIS("SJIS")でエンコーディングした場合に何バイトになるかは、
以下のようにして調べることができます。
int bytes = "あいうえお".getBytes("SJIS").length();
エンコーディングとして指定できる値の一覧は、以下のURLで得ることができます。
http://java.sun.com/j2se/1.3/docs/guide/intl/encoding.doc.html
なお、String#getBytes(String encoding) は不当な値を指定した場合に
java.io.UnsupportedEncodingException をthrowするので、
実際のコードでは例外を適切に処理する必要があります。
[S008 Q-09]
文字列に対してメソッド呼び出しが行なえるのはなぜですか?
[S008 A-09]
文字列はすべてjava.lang.Stringクラスのインスタンスだからです。
S008-01「文字列の内容の比較はどのように行えば良いのでしょうか?」で説明したよう
に、文字列リテラルも実体はjava.lang.Stringクラスのインスタンスです。
このため、文字列リテラルに対してjava.lang.Stringクラスに定義されているメソッド
を呼び出すことができます。
例えば、以下のように文字列の長さを得ることができます。
int length = "あいうえお".length();
[S008 Q-10]
CSV データなど "," で区切られた文字列を、個々のデータに切り分けたいのですが?
[S008 A-10]
java.util.StringTokenizer を用いるのが簡単です。
java.util.StringTokenizerは、区切り文字を指定して
文字列をトークン(単語)に分解するためのクラスです。
個々のトークンにアクセスするために java.util.Enumeration
インタフェースを実装しています。具体的には、以下
のように利用します。デリミタとして、"," を指定する
場合です。
StringTokenizer st = new StringTokenizer("001,red,909,text", ",");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
コンストラクタで、分解する文字列を指定します。コンス
トラクタでデリミタを指定しなかった場合は空白文字がデ
リミタとなります。上記の実行結果は以下のようになります。
001
red
909
text
ただしこのクラスは、あくまで文字列をデリミタで区切って分解するためのものでしかありません。
例えばCSVの仕様では""で囲われた , は区切り文字とは見なされないのですが、
その辺の実装は独自に工夫しなければいけません。以下のコードを実行しても "text,word" となるべきところが、
"text と word" とに分割されてしまい、期待した結果は得られません。
StringTokenizer st =
new StringTokenizer("001,red,909,\"text,word\"",",");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
詳しくは JDK の API リファレンスを参照してください。
参考記事 [JavaHouse-Brewers:25967]
[S008 Q-11]
new StringBuffer().append("a").append(4).append("c").toString() という
使い方は?
[S008 A-11]
StringBuffer#appned は、文字列に文字(列)等を追加した後の自分自身
への参照を返します。ですから
new StringBuffer().append("a").append(4).append("c").toString()
は以下のようなコードと同等です。
StringBuffer sb = StringBuffer(); //空の StringBuffer を作る。
sb = sb.append("a"); //StringBuffer に "a" を追加。
sb = sb.append(4); //StringBuffer に "4" を追加。
sb = sb.append("c"); //StringBuffer に "c" を追加。
sb.toString(); //String "a4c" を表す。
参考記事 [JavaHouse-Brewers:8449]
[S008 Q-12]
StringTokenizerでいきなり2番目の要素を取り出したいのですが?
[S008 A-12]
あきらめて下さい。
どうしてもという場合は
public SmartStringTokenizer extends StringTokenizer {
public String getTokenAtIndex(int n){
:
}
と、自分でそういうクラスを拡張するなりして下さい。
参考記事 [JavaHouse-Brewers:1165]
[S008 Q-13]
同じ内容の文字列リテラルは定数なんですからオブジェクトは共用されないのでしょうか?
[S008 A-13]
はい、されます。
詳しくは S008-1 を参照してください。なお
定数(リテラル)でない、String オブジェクトから、同じ内容の
リテラルへの参照を得るには intern メソッドを使います。
String オブジェクト s1,s2 について、s1 != s2 でも s1.equals(s2)
が true なら、s1.intern() == s2.intern() となります。
String#intern() メソッドは String オブジェクトの定数文字列
(リテラル)をシステムが持つ一意な共用プールから参照します。
もしそのリテラルが存在しない場合に新規に作成・登録したものへ
の参照を返します。先の定数文字列の宣言
String str1 = "abcde"
は String str = new String("abcde").intern() の省略表現である
という事です。
詳しい解説は
Java言語仕様 (JLS) 第 2版 p.27
3.10.5 文字列リテラル
にあります。
[S008 Q-14]
文字列オブジェクト「"abcdefg"」の意味は?
[S008 A-14]
S008-9の特に後半部の説明を参照して下さい。
contributor: Ryuji Hattori
コメントの送り先:Java FAQ BBS