【Java】文字列結合・標準入力 (AOJ②複数の出力・テストケースの出力)


AIZU ONLINE JUDGE の教材を使って勉強していきます


#文字列結合
* **文字列は演算子で結合できる**
    * `result += "bar";`
    * `builder.append("bar");` //めっちゃ早い
* + で文字列を結合すると、**元の文字列、連結する文字列、結果文字列の3個のString文字列を生成しているのでガベージコレクションの増大**に。。
    * 元の文字列、連結する文字列の `"ABC"` `"DEF"`は両方メモリのある領域に確保されていて
    * +するとメモリ領域に`"ABCDEF"`は新しく別の領域に確保されてる
    * **確保しながら足していくというコストのかかるoutputになっている。。。**
* →StringBuilderクラスの**appendメソッドを使うと、一定のサイズを確保した可変長の文字列を表すので、確保した領域で文字列を自由に変更**できる
    * appendは何をしたいかのみ蓄えて最後一回のみ結合するということ
        * `"ABC"`　`"DEF"` `"GHI"`  足したいものを蓄えて、最後一回のみ結合
        * `"ABCDEFGHI"`
* **→インスタンスの生成・破棄が少ない！**
* 最新のJavaでは+ 演算子は内部的にStringBuilderに変換されるので単発の文字列連結の場合+演算子でOK

##時間比較
100000文字連結した結果、stringBuilderが7msで圧勝！


```Java
import java.util.Date;

public class Main {
    public static void main(String[] args) { new Main().run(); }
    private void run(){
        long startTime = getNow();
        String hoge = "";
        for (int i= 0; i < 100000; i++){
            hoge  = hoge + "mohu";
        }
        long elapse = getNow() - startTime;
        System.out.println("elapse "+ elapse ); //2716ms
    }
    //Dateで今の時間をとる
    private long getNow(){
        return new Date().getTime();
    }
}
```

```Java
import java.util.Date;

public class Main {
    public static void main(String[] args) { new Main().run(); }
    private void run(){
        long startTime = getNow();
         StringBuilder stringBuilder = new StringBuilder();
        for (int i= 0; i < 100000; i++){
            stringBuilder.append("mohu");
        }
        long elapse = getNow() - startTime;
        System.out.println("elapse "+ elapse ); //7ms
    }
    //Dateで今の時間をとる
    private long getNow(){
        return new Date().getTime();
    }
}
```

##複数の Hello World の出力
> 1000 個の "Hello World" を出力するプログラムを作成して下さい。

* 注意：AOJはJava9より下位JDKを使って動作しているので**型推論varは動作しない**（①）
    * 変数名(symbol) builderが定義されていないというコンパイルエラーになる
    * Aizu Online ではコンパイルエラーだと **[CE]**

```java
//①AOJでは動かないが正しいコード
//cannot find symbol var builder = new StringBuilder();
public class Main{
    public static void main(String[] args){
        var builder = new StringBuilder();
        for (var i=0;  i<1000; i++){
        	builder.append("Hello World\n");
        }
        var result = builder.toString();
        System.out.println(result);
    }
}

```

```java
//②AOJで動きます
public class Main {

	public static final int BIG_NUM  = 2000000000;
	public static final int MOD  = 1000000007;

	public static void main(String[] args) {
		StringBuilder ans = new StringBuilder();
		for(int i = 0; i < 1000; i++){
            ans.append("Hello World\n");
        }
		System.out.print(ans.toString());
	}
}
```

# BufferedReaderとScannerの違い
* **Scannerは遅いしメモリを食う問題。。。**
* 高速化が必要な場合は**BufferedReader.readLineで行ごとに読み込む**
* 比較
    * **BufferedReader**の方がScannerより**パフォーマンスが優れている**
    * **BufferedReaderは文字列しか受け取れない**がScannerは数値も直接受け取れる
        * BufferedReader数値を扱いたい場合は、文字列を受け取ったあとに数値に変換
        * **ScannerはnextInt()メソッドを使うことで数値を直接受け取れる**
    * **BufferedReaderはチェック例外の対処が必要**（IOException）だが、Scannerは不要
        * BufferedReaderは**チェック例外の対処（try-catch文など）が必要**


##テストケースの出力
>オンラインジャッジでは、提出されたプログラムが複数の入力データそれぞれに対して正しい出力を行っているかを判定するために、１つの入力データファイルに複数のデータセットが含まれているものがあります。この問題は、そのようなデータセットを処理するための練習問題です。
１つの整数 x を読み込み、それをそのまま出力するプログラムを作成して下さい。
Output　各データセットごとに、以下の形式で x を出力して下さい：
Case i: x

```Java
//実行時間18s
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int i=0;
		while(true){
			i++;
			int k = Integer.parseInt(br.readLine());
			if(k == 0){break;}
			System.out.println("Case "+i+": "+k);
		}
	}
}
```

```Java
//実行時間31s
import java.util.Scanner;
public class Main {
	public static void main(String[] args){
		Scanner in = new Scanner(System.in);
		for(int i=1;;i++){
			int num=in.nextInt();
			if(num==0)
				break;
			System.out.println("Case "+i+": "+num);
		}
	}
}
```