【Java初心者用】配列からCSVデータを作成する
プログラムの実行結果をCSV形式に変換し、ファイルに保存しなければならない時があります。各データの後に「,」の文字を連結して作成すれば良いのですが、配列にデータがあらかじめ格納されている場合は「join()」メソッドを利用すれば簡単に作成することができます。今回は配列に格納されたデータを使ってCSV形式のデータを作成する方法を説明します。
まず、この記事は以下のような人を対象としています。
対象者・Javaの勉強を始めたばかりの初心者
・配列からCSV形式のデータの作成方法を知りたい人
・join()メソッドの使い方を知りたい人
・「+」演算子による文字列連結の仕組みを知りたい人
・StringBuilderクラスを使って文字列連結方法を知りたい人
この記事を読むと、次のようなことが理解できるようになります。
この記事を読むとできること・jon()メソッドの使い方を理解することができる
・文字列が格納されている配列からCSV形式のデータの作成方法を知ることができる
・数値が格納されている配列からCSV形式のデータの作成方法を知ることができる
・「+」演算子とStringBuilderクラスの違いについて理解することができる
配列内のデータを使ってCSV形式のデータを作成する必要があり、join()メソッドを勉強しているのですが質問があります。
join()メソッドを使うとCSV形式のデータを簡単に作成できますね。
困っていることは何ですか?
配列にint型のデータが格納されている場合、join()を使うとコンパイルエラーになります。どうしたら良いですか?
join()メソッドは文字列のデータしか扱うことができません。
自分でプログラムを考えないといけませんね。
なお、CSV形式のデータを使って合計や平均などを求めるプログラムの作り方は以下の記事で紹介しています。
join()メソッド
Javaには、文字列を処理するために色々なメソッドが定義されています。
join()メソッドは、「配列内のデータを指定した区切り記号で連結して1つの文字列を作成する」メソッドです。このメソッドを使えば、配列から簡単にCSV形式のデータを作成することができます。
join()メソッドの書式は以下の通りです。
public static String join(CharSequence delimiter, CharSequence… elements)
第1引数には、連結する時に利用する記号を指定します。
第2引数には、連結したいデータを複数の文字列で指定します。複数の文字列を指定しても、文字列が格納されている配列を指定しても構いません。また、文字列が格納されたArrayList(コレクション)も指定することができます。
なお、「CharSequence」はインタフェースです。初心者の人は、文字列と同じと考えて構いません。
このメソッドを利用する場合、注意することが2点あります。
注意点1 このメソッドはstaticメソッドである
メソッドの定義部分を確認すると「static」が記述されているので、このメソッドはstaticメソッドとなります。staticメソッドとは、インスタンスを作成せずに利用できるメソッドで、利用する場合にメソッド名の前にクラス名のStringを付ける必要がある特別なメソッドです。
注意点2 引数には文字列を指定しなければならない
join()メソッドの第2引数には文字列のデータしか指定することはできません。文字列データであれば、文字列そのものでも、配列でもコレクションでも指定することができます。しかし、数値(int型など)のデータを指定するとコンパイルエラーとなります。
文字列を使ったサンプルプログラム
以下のプログラムは第2引数に文字列を指定した場合のサンプルです。文字列のデータを利用しているのでコンパイルエラーにならず、正しい結果が表示されていることが分かります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package csvAnalysis; import java.util.ArrayList; import java.util.List; public class CsvFromArray { public static void main(String[] args) { // 第2引数以降に直接連結したい文字列を指定した場合 String sample1 = String.join(",","Java","C#","Python"); // 予め連結した文字列を配列に格納し、第2引数で指定した場合 String[] dataArray = {"Apple","Orange","Banana"}; String sample2 = String.join(",",dataArray); // 予め連結したい文字列をArrayListに格納し、第2引数で指定した場合 List<String> dataList = new ArrayList<>(); dataList.add("東京"); dataList.add("名古屋"); dataList.add("大阪"); String sample3 = String.join(",", dataList); System.out.println(sample1); System.out.println(sample2); System.out.println(sample3); } } |
11行目では、join()メソッドの第2引数に直接複数の文字列を指定しています。
14行目から15行目では、文字列データをString型の配列に代入し、その配列をjoin()メソッドの第2引数に指定しています。
18行目から22行目では、文字列データをArrayListコレクションに代入し、そのArrayListをjoin()メソッドの第2引数に指定しています。
第2引数に文字列データを指定していれば、どの方法でも構いません。
実行結果は以下の通りです。
数値を使ったサンプルプログラム
数値のデータを配列に格納しておき、join()メソッドの第2引数に指定するとどうなるでしょうか? 以下のようにコンパイルエラーとなります。
では、数値のデータが格納された配列を使ってCSV形式のデータを作成するにはどのようにすれば良いでしょうか?
join()メソッドの代わりに利用でいるメソッドが存在しないため、面倒ですが自分でプログラムを作成する必要があります。そこで、今回は配列内の数値データを利用してCSV形式のデータ(文字列)を作成するプログラムのサンプルをいくつか紹介します。
基本的な考え方は、配列からデータ(数値)を1個取り出し、その数値に文字列「,」を連結させます。
間違えているサンプルプログラム(最後に「,」が余計に付けられてしまう)
以下に、最初に考えられるプログラムを示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package csvAnalysis; public class CsvFromIntArray { public static void main(String[] args) { int[] dataArray = { 10, 20, 30, }; // 数値の配列に対してjoin()メソッドは使えない //String sample = String.join(",", dataArray); // 【方法1】配列から1個データを取り出し、「,」を連結 String csvStr1 = ""; // 連結したCSVデータを保存 for (int data : dataArray) { // 取り出した整数に「,」を連結し、前回の文字と連結 csvStr1 += data + ","; } System.out.println(csvStr1); } } |
実行結果は以下の通りです。
13行目から17行目までの処理を図で説明すると以下のようになります。
一見正しい結果のように見えますが、最後のデータの後にも「,」が追加されています。したがって、単純に配列からデータを取り出して「,」と連結しただけでは正しい結果が得られません。
この問題を解決するためには、配列から取り出したデータが最後のデータかどうかをチェックし、最後のデータの時は「,」と連結しないというプログラムを作成します。そのサンプルプログラムが以下のようになります。
最後のデータの時だけ「,」を付けないサンプルプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package csvAnalysis; public class CsvFromIntArray { public static void main(String[] args) { int[] dataArray = { 10, 20, 30, }; // 数値の配列に対してjoin()メソッドは使えない //String sample = String.join(",", dataArray); // 【方法1】配列から1個データを取り出し、「,」を連結 String csvStr1 = ""; // 連結したCSVデータを保存 for (int data : dataArray) { // 取り出した整数に「,」を連結し、前回の文字と連結 csvStr1 += data + ","; } System.out.println(csvStr1); // 【方法2】最後のデータの時だけ「,」をつけない String csvStr2 = ""; for (int i = 0; i < dataArray.length; i++) { // 最後のデータではない場合 if (i != dataArray.length - 1) { // 取り出したデータと[,」を連結 csvStr2 += dataArray[i] + ","; } else { // 最後のデータの場合はデータのみ連結 csvStr2 += dataArray[i]; } } System.out.println(csvStr2); } } |
実行結果は以下の通りです。方法2では最後に余計な「,」が付けられていないことが分かります。
この方法では、データを配列から取り出す時に拡張for文ではなく通常のfor文を利用しています。配列から全てのデータを1個ずつ取り出す場合、拡張for文の方が簡単に記述できるのですが、何個目のデータを利用しているのかを調べる方法がありません。したがって、拡張for文の場合は配列内の最後のデータか判断することができません。そこで、方法2では通常のfor文を使い、最後のデータかどうかはfor文のカウンタ変数「i」で判定しています。
最後のデータの添え字は「配列の要素数-1」になることに注意が必要です。配列dataArrayにはデータが3個格納されているので、要素数(dataArray.length)は「3」ですが、最後のデータ(30)の添え字は「2」となります。これは、配列の添え字が1からではなく0から始まっているためです。配列を利用する場合は、添え字の値に注意が必要です。
文字列連結にStringBuilderクラスを利用したサンプル
一般的に多くのプログラム言語では文字列の連結に「+」演算子を利用します。この方法は非常に簡単なのですが、Javaの場合は連結処理が多くなればコンピュータに負担がかかります。そのためJavaでは文字列を連結するために利用できる「StringBuilderクラス」が用意されています。
「+」演算子を使って文字列を連結するプログラムでは、ある文字の末尾に新しい文字列が連結されるように思えるかもしれませんが、実際には末尾に追加することはできません。Javaでは「文字列(Stringオブジェクト)はイミュータブル(immutable)なオブジェクト」であり、一度作成したものを後から変更することはできないようになっています。したがって、末尾に追加することは不可能なのです。
では、「+」演算子で文字列を連結する処理はパソコン内部でどのように処理されているのでしょうか? 文字列の連結と言っていますが、実際は全く新しい文字列を作成しています。以下が「+」演算子を使って文字列の連結をおこなった時のパソコン内部(メモリ)での状態です。
- 文字列「abc」が格納されているStringオブジェクトをメモリ内に作成する
- そのStringオブジェクトの場所を示す情報(メモリアドレス)を変数strに代入する
- 全く異なる場所に文字列「123」が格納されているStringオブジェクトを作成する
- 2つのStringオブジェクトを使って、全く異なる場所に文字列「abc123」が格納されているStringオブジェクトを作成する
- 作成した新しいStringオブジェクトの場所を示す情報(メモリアドレス)を変数strに代入する
「+」演算子を使って文字列の連結をおこなうと、内部ではこのような処理がおこなわれています。ある文字に別の文字を連結するとメモリ内では合計3つのStringオブジェクトが作成されることになります。Stringオブジェクトはメモリを消費するので連結する文字列が多ければ多いほど、大量のメモリを消費してしまいパソコンに負担がかかってしまいます。
作成したStringオブジェクトは「GC(ガーベージコレクション)」と呼ばれるメモリの掃除をするプログラムによって自動的に解放されるのですが、いつメモリが解放されるかは決まっていません。したがって、短期間で大量の文字列連結を「+」演算子でおこなうと、メモリ解放が追い付かずにメモリ不足に陥る危険性があります。
一方、StringBuilderクラスを利用した場合はこのようなメモリ不足になる危険性はほとんどありません。「StringBuilderオブジェクトはミュータブル(mutable)なオブジェクト」であり、作成後に内容を変更することが可能となっています。つまり、作成したStringBuilderオブジェクトの末尾に直接文字列を連結させることができるのです。
以下が文字列連結をStringBuilderクラスを使っておこなっているサンプルプログラムです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
package csvAnalysis; public class CsvFromIntArray { public static void main(String[] args) { int[] dataArray = { 10, 20, 30, }; // 数値の配列に対してjoin()メソッドは使えない //String sample = String.join(",", dataArray); // 【方法1】配列から1個データを取り出し、「,」を連結 String csvStr1 = ""; // 連結したCSVデータを保存 for (int data : dataArray) { // 取り出した整数に「,」を連結し、前回の文字と連結 csvStr1 += data + ","; } System.out.println(csvStr1); // 【方法2】最後のデータの時だけ「,」をつけない String csvStr2 = ""; for (int i = 0; i < dataArray.length; i++) { // 最後のデータではない場合 if (i != dataArray.length - 1) { // 取り出したデータと[,」を連結 csvStr2 += dataArray[i] + ","; } else { // 最後のデータの場合はデータのみ連結 csvStr2 += dataArray[i]; } } System.out.println(csvStr2); // 【方法3】文字列連結にStringBuilderを利用する StringBuilder csvStr3 = new StringBuilder(); for (int i = 0; i < dataArray.length; i++) { // 取り出したデータをStringBuilderの末尾に追加 csvStr3.append(dataArray[i]); // 最後のデータではない場合 if (i != dataArray.length - 1) { // 「,」をStringBuilderの末尾に追加 csvStr3.append(","); } } System.out.println(csvStr3); } } |
実行結果は以下の通りです。
35行目で中身が空っぽのStringBuilderオブジェクトを作成しています。文字列を末尾に追加するには「append(追加したい文字列)」メソッドを実行します。「+」演算子の場合はデータ(数値)と「,」をまとめて連結していましたが、このサンプルではデータ(数値)と「,」は分けてそれぞれ連結されています。
まず配列からデータを1つ取り出しStringBuilderオブジェクトの末尾に連結します(38行目)。次にif文を利用して、連結したデータが最後のデータかどうかをチェックしています。もし最後のデータでない場合は「,」をStringBuilderオブジェクトの末尾に連結します(42行目)。最後のデータの場合は何もしません。
文字列の連結をおこなう場合、StringBuilderクラスを利用するプログラムの書き方にも慣れておいた方が良いでしょう。
join()メソッドを利用するサンプル
最後のサンプルはjoin()メソッドを使うプログラムです。「えっ? 数値の場合はjoin()が使えないんじゃないの?」と疑問に思うかもしれません。確かに数値の場合はjoin()は使えません。逆転の発想で「数値を文字列に変換して配列に入れなおせば、join()メソッドが使える」ようになります。
CSV形式データは文字列なので最終的にプログラム内で配列内のデータ(数値)を文字列に直さないといけません。であれば、数値が格納されている配列の他に別途、数字(文字列)に変換した配列を作ってしまい、join()メソッドでその配列を利用するという考え方です。
プログラムで問題を解く場合、正解は1つだけではありません。したがってこのプログラムも目的の結果(CSV形式のデータ)が得られているので間違いではありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
package csvAnalysis; public class CsvFromIntArray { public static void main(String[] args) { int[] dataArray = { 10, 20, 30, }; // 数値の配列に対してjoin()メソッドは使えない //String sample = String.join(",", dataArray); // 【方法1】配列から1個データを取り出し、「,」を連結 String csvStr1 = ""; // 連結したCSVデータを保存 for (int data : dataArray) { // 取り出した整数に「,」を連結し、前回の文字と連結 csvStr1 += data + ","; } System.out.println(csvStr1); // 【方法2】最後のデータの時だけ「,」をつけない String csvStr2 = ""; for (int i = 0; i < dataArray.length; i++) { // 最後のデータではない場合 if (i != dataArray.length - 1) { // 取り出したデータと[,」を連結 csvStr2 += dataArray[i] + ","; } else { // 最後のデータの場合はデータのみ連結 csvStr2 += dataArray[i]; } } System.out.println(csvStr2); // 【方法3】文字列連結にStringBuilderを利用する StringBuilder csvStr3 = new StringBuilder(); for (int i = 0; i < dataArray.length; i++) { // 取り出したデータをStringBuilderの末尾に追加 csvStr3.append(dataArray[i]); // 最後のデータではない場合 if (i != dataArray.length - 1) { // 「,」をStringBuilderの末尾に追加 csvStr3.append(","); } } System.out.println(csvStr3); // 【方法4】一度文字列配列に変換し、join()メソッドを利用する // 数値が格納されている配列と同じ長さの空の配列を作成 String[] strArray = new String[dataArray.length]; // 配列からデータを1個ずつ取り出す for (int i = 0; i < dataArray.length; i++) { // 文字列に変換し、別の配列に代入 strArray[i] = String.valueOf(dataArray[i]); } System.out.println(String.join(",", strArray)); } } |
実行結果は以下の通りです。
49行目で文字列に変換したデータを保存する新しい配列をString型として作成しています。なお、配列は作成する際に要素数を必ず指定しなければなりません。今回は数値データを全て文字列に変換して配列に格納するので、数値データが格納されている配列のサイズを指定しています。
53行目では、配列から取り出された数値データを「String.valueOf()」メソッドを使って文字列に変換し、その文字列を新しく作成した配列に代入しています。この処理の結果、全ての数値データが文字列データに変換され、別の配列に代入されます。
55行目でjoin()メソッドを使って、CSV形式のデータに変換し表示しています。
まとめ
今回は、数値が格納されている配列のデータからCSV形式のデータを作成する方法について説明しました。
目的の結果を得るためのプログラムは1つだけではありません。今回は色々なプログラムの作り方について説明しました。プログラマは色々な解き方(プログラムの作り方)を知っておく必要があるので、今回のサンプルプログラムをぜひ参考にして下さい。
・数値が格納されている配列にはjoin()メソッドは直接利用できない
・join()メソッドを利用したい場合、一度文字列配列に変換しなければならない
・文字列の連結は、StringBuilderクラスを利用すると良い
数値が格納された配列からCSV形式のデータを作成するサンプルプログラムはいかがでしか?
改めて、色々なプログラムの書き方があるのが分かりました!!
やはり、join()メソッドを使う方が楽な気がします。
1つのサンプルだけではなく、色々なプログラムの作り方を確認すれば、必ずプログラミングスキルが向上しますよ!!