【Java入門】文字列の扱いについて（Stringクラス、StringBuilderクラス）

#目的

Java言語を含めたプログラミングの学習を始めたばかりの方、既学習者の方は復習用に、
今回は**文字列の操作**について学ぶために書いています。

[【Java入門目次】](https://qiita.com/morioheisei/items/6c7fc063c26e63d890ac)
・[変数と型](https://qiita.com/morioheisei/items/3d27e82d6cd88068dded)
・[型変換](https://qiita.com/morioheisei/items/28ab5fd69570e01cd317) 
・[変数のスコープ](https://qiita.com/morioheisei/items/c56ab32b68ce92d32206)
・文字列の操作 ←今ここ
・[配列の操作](https://qiita.com/morioheisei/items/b6acbb1b0c07b40e7f56)
・[演算子](https://qiita.com/morioheisei/items/63088c61e8234dc1f7f5)
・[条件分岐](https://qiita.com/morioheisei/items/194cdc73a29bddade0a4)
・[繰り返し処理](https://qiita.com/morioheisei/items/e686e33b60c6a60e87f0)
・クラスについて（準備中）
・抽象クラス（準備中）
・インターフェース（準備中）
・カプセル化（準備中）
・モジュールについて（準備中）
・[例外処理について](https://qiita.com/morioheisei/items/d25d2b67e7530c1aab9d)
・[ラムダ式について](https://qiita.com/morioheisei/items/2bfcc3d8d8440b00e6a9)
・[Stream APIについて](https://qiita.com/morioheisei/items/3a7be38b4d3ef712ccb2)

#Stringクラスとは

複数の文字を格納する事が出来るデータ型として提供されているクラスです。（文字列を扱うクラス）

`""`で文字列を囲み変数に代入しているのをよく目にするかと思います。

```java:
String msg = "Hello World!!!";
System.out.println(msg); // Hello World!!!
```

`new String(文字列)`と記述して、明示的に変数に代入する事も出来ます。
また、char型の配列をString型の文字列にする事もできます。

```java:
String banana = new String("バナナ");
System.out.println(banana); // バナナ

// char型の配列を渡してもOK
String tomato;
char[] chars = {'ト', 'マ', 'ト'};
tomato = new String(chars);
System.out.println(tomato); // トマト
```


Stringクラスは、初期化されたString型の変数に異なる文字列を再代入すると、元の文字列が書き換わるのではなく、
**immutable（不変）なオブジェクトですので、**参照先が変わっている（新たに別の領域を確保している）という特徴があります。

同じ参照型の配列（ミュータブル）と比べてみてみましょう。

```java:
// イミュータブル（不変）の例
String a = "あ";
String b = a;
a = "い";
System.out.println(a); // い
System.out.println(b); // あ

// ミュータブル（可変）の例
int[] x = {1, 2, 3};
int[] y = x;
x[0] = 100;

for (int xVal : x) {
  System.out.print(xVal + " "); // 100 2 3
}

for (int yVal : y) {
  System.out.print(yVal + " "); // 100 2 3
}
```

##Stringクラスのメソッド紹介

Stringクラスが提供してくれているメソッドはたくさんあります。
構文は`String型の変数名.メソッド名();`です。

いくつか紹介していきます。


###charAt

引数で指定されたインデックスにあるchar型を返してくれるメソッドです。
引数が負の値、文字列の長さと同じか大きい値の場合、例外が発生します。

```java:
String name = "ケイスケホンダ";
System.out.println(name.charAt(3)); // ケ

// 以下は例外が発生します
System.out.println(name.charAt(7)); // 例外が発生
System.out.println(name.charAt(10)); // 例外が発生
```

###concat

指定された文字列を最後に連結するメソッドです。

```java:
String greet = "おはよう";
System.out.println(greet.concat("ございます。")); // おはようございます。
```

###equals

文字列と引数で指定されたオブジェクトを比較してbooleanを返してくれるメソッドです。

```java:
String name = "ケイスケホンダ";
System.out.println(name.equals("リトルホンダ")); // false
System.out.println(name.equals("ケイスケホンダ")); // true
```

###indexOf

引数の文字が最初に出現する位置のインデックスを返してくれるメソッドです。
該当する文字が文字列にない場合は-1を返します。

```java:
String str = "banana";
System.out.println(str.indexOf('a')); // 1
System.out.println(str.indexOf('z')); // -1
```

###length

文字列の長さを返してくれるメソッドです。

```java:
String name = "ジェームズ・ハーデン";
System.out.println(name.length()); // 10
```

###replace

第一引数の文字を第二引数で指定した文字に置き換え、結果の文字列を返してくれるメソッドです。

```java:
String name = "ボボボーボ・ボーボボ";
System.out.println(name.replace("ボ", "バ")); // バババーバ・バーババ
```

###isEmpty

length()が0の場合にのみtrueを返してくれるメソッドです。

```java:
String teruma = "ここにいるよ";
System.out.println(teruma.isEmpty()); // false
String none = "";
System.out.println(none.isEmpty()); // true
```

メソッド数が多すぎて全て紹介しきれないので、その他のメソッドは[OracleのStringクラス](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/String.html)を参考にしてください。


#StringBuilderクラスとは

Stringクラスと同様に文字列を扱うクラスです。
変数に格納した文字列を変更する事が可能です。（元の文字列を書き換える事が出来る）
何故ならStringBuilderクラスは**mutable（可変）なオブジェクトだから**です。

StringBuilder型の文字列を作成するには、配列作成時と同じように、`newキーワード`を使用します。

```java:
StringBuilder sb = new StringBuilder("Hello World!!!");
System.out.println(sb); // Hello World!!!
```

Stringクラスの様に直接代入しようとするとエラーになります。

```java:エラー
StringBuilder name = "田中"; // Type mismatch: cannot convert from String to StringBuilder
```


##StringBuilderクラスのメソッド紹介

StringBuilderクラスが提供してくれているメソッドもたくさんあります。
構文はStringクラス同様に`StringBuilder型の変数名.メソッド名();`です。

いくつか紹介していきます。

###append

引数で指定された文字列を現在の文字列に追加するメソッドです。

```java:
StringBuilder name = new StringBuilder("田中");
System.out.println(name); // 田中
name.append("マルクス");
System.out.println(name); // 田中マルクス
name.append("闘莉王");
System.out.println(name); // 田中マルクス闘莉王
```

###insert

引数で指定された文字列を引数で指定された位置にある文字の前に挿入するメソッドです。

```java:
StringBuilder member = new StringBuilder("柳沢、巻");
member.insert(3, "玉田..."); // 3なので、"巻"の前が挿入位置となる
System.out.println(member); // 柳沢、玉田...巻
```

###delete

第一引数から第二引数の位置の１つ前までの位置にある文字を削除するメソッドです。

```java:
StringBuilder member = new StringBuilder("クラウド、エアリス、ティファ、バレット");
member.delete(5, 10); // 開始位置5なので「エ」、終了位置は10なので「、」
System.out.println(member); // クラウド、ティファ、バレット
```

###replace

第一引数から第二引数の位置の１つ前までの位置にある文字を、第三引数で指定された文字列に置換するメソッドです。

```java:
StringBuilder sb = new StringBuilder("デュフフフ!!");
sb.replace(2, 5, "ラララ"); // 開始位置2なので「フ」から、終了位置5なので「フ」までの文字列を第三引数の文字「ラララ」に置換する
System.out.println(sb); // デュラララ!!
```

###substring

引数で指定した位置から最後までの部分文字列を返してくれるメソッドです。

```java:
StringBuilder oosako = new StringBuilder("大迫半端ないって");

// インデックス2番目から最後までの文字列を返している
System.out.println(oosako.substring(2)); // 半端ないって
```

StringBuilderクラスもメソッド数が多いため全て紹介しきれないので、その他のメソッドは[OracleのStringBuilderクラス](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/StringBuilder.html)を参考にしてください。


#文字列の結合方法まとめ

文字列を結合する方法は複数種類がある事が分かりました。

```java:プラス演算子
String str1 = "Hello";
String str2 = "World!!!";
String greet = str1 + str2;
System.out.println(greet); // HelloWorld!!!
```

```java:Stringクラスのconcatメソッド
String str = "Hello";
String greet2 = str.concat("World!!!");
System.out.println(greet2); // HelloWorld!!!
```

```java:StringBuilderクラスのappendメソッド
StringBuilder sb = new StringBuilder("Hello");
StringBuilder greet3 = sb.append("World!!!");
System.out.println(greet3); // HelloWorld!!!
```

出力結果は同じですが、それまでの過程が違います。

#文字列比較の注意点

では、`==演算子`を用いて各変数を比較してみます。
出力結果が同じですので、trueになると思いきや、


```java:
// プラス演算子
String str1 = "Hello";
String str2 = "World!!!";
String greet = str1 + str2;
System.out.println(greet); // HelloWorld!!!

// Stringクラスのconcatメソッド
String str = "Hello";
String greet2 = str.concat("World!!!");
System.out.println(greet2); // HelloWorld!!!

System.out.println(greet == greet2); // false
```

**HelloWorld!!!**という文字列は同じですが、falseとなっています。
`==演算子を用いた参照型の比較では、参照先が同じかどうかを比較しているためこのような結果になってしまいます。`



また、文字列の宣言方法によっても比較結果が変わってきます。

**new String()で文字列を作成した場合**、必ず新しい領域にその文字列は生成されます。
それ以外の場合は、同じ文字列が既に存在していれば、既存の文字列を参照します。
よって、`==演算子`で比較した場合以下のような結果になります。


```java:
String str1 = "山田";
String str2 = new String("山田");
String str3 = "山田";

System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // true
```




Stringクラスで文字列を比較したい場合は、`equalsメソッド`を用いて比較します。

```java:
// プラス演算子
String str1 = "Hello";
String str2 = "World!!!";
String greet = str1 + str2;
System.out.println(greet); // HelloWorld!!!

// Stringクラスのconcatメソッド
String str = "Hello";
String greet2 = str.concat("World!!!");
System.out.println(greet2); // HelloWorld!!!

// 参照先の比較ではなく、変数の中の文字列を比較している
System.out.println(greet.equals(greet2)); // true
```


ちなみに、StringBuilderは既存の文字列が書き換わるため、
sbという変数とgreet3という変数を比較するとtrueが返ってきます。

```java:
StringBuilder sb = new StringBuilder("Hello");
StringBuilder greet3 = sb.append("World!!!");
System.out.println(greet3); // HelloWorld!!!

System.out.println(sb == greet3); // true
```



#終わりに

文字列を扱うためのStringクラス、StringBuilderクラスについて学びました。

文字列の比較する際は、意図せぬ処理にならないよう注意しなければいけませんね。
また本記事では紹介する事の出来なかった各クラスのメソッドも、理解して扱えるようにしておきたいですね。

#参考サイト

**[OracleのStringクラス](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/String.html)**
**[OracleのStringBuilderクラス](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/StringBuilder.html)**
**[文字列と文字列の比較](https://www.javadrive.jp/start/string/index4.html)**