MyStringArrayListの結果確認。
練習問題19-3で、作成したMyStringArrayListの結果確認のため、MyStringArrayListにtoString()メソッドを追加し、UseMyStringArrayListクラスを作成しました。
まず、MyStringArrayListクラスのtoString()メソッド。
public String toString() { int size = size(); String s = "サイズ = " + size + ", 長さ = " + ar.length + ", 値 = ["; if (size != 0) { s = s + ar[0]; } for (int i = 1; i < size; i++) s = s + " " + ar[i]; s = s + "]"; return s; }
次に、UseMyStringArrayListクラス。
public class UseMyStringArrayList { public static void main(String[] args) { MyStringArrayList list = new MyStringArrayList(); System.out.println(list); list.add("1番目"); list.add("2番目"); list.add("3番目"); list.add("4番目"); System.out.println(list); list.add("5番目"); System.out.println(list); } }
実行結果は、こんな感じになります。
E:\study\19>javac MyStringArrayList.java UseMyStringArrayList.java E:\study\19>java UseMyStringArrayList サイズ = 0, 長さ = 4, 値 = [] サイズ = 4, 長さ = 8, 値 = [1番目 2番目 3番目 4番目] サイズ = 5, 長さ = 8, 値 = [1番目 2番目 3番目 4番目 5番目]
...あれ、サイズ4の時はまだ長さ4になってないとおかしいですね。MyStringArrayListを見直して...。
E:\study\19>java UseMyStringArrayList サイズ = 0, 長さ = 4, 値 = [] サイズ = 4, 長さ = 4, 値 = [1番目 2番目 3番目 4番目] サイズ = 5, 長さ = 8, 値 = [1番目 2番目 3番目 4番目 5番目]
うん、OK。確認重要ということですね。
Set、Map、Map.EntryのtoString();
「ArrayListのtoString();」を試したついでに、Set、Map、Map.EntryのtoString();も確認しておきます。
import java.util.*; public class HashTest { public static void main(String[] args) { Set<String> set = new HashSet<String>(); set.add("Alice"); set.add("Bob"); set.add("Chris"); Map<String,String> map = new HashMap<String,String>(); map.put("Alice", "Girl"); map.put("Bob", "Boy"); map.put("Chris", "Man"); System.out.println("Set<String> : " + set); System.out.println("Map<String,String> : " + map); for (Map.Entry<String,String> entry : map.entrySet()) { System.out.println("MapEntry<String,String> : " + entry); break; } } }
E:\study\19>java HashTest Set<String> : [Bob, Chris, Alice] Map<String,String> : {Bob=Boy, Chris=Man, Alice=Girl} MapEntry<String,String> : Bob=Boy
うん、見れば分かる感じですね。
ArrayListのtoString();
リスト19-10でLinkedListインスタンスをSysytem.out.printlnの引数にして、次のような出力を得ています。
[Bob, Chris, Diana, Elmo]
もしかして、「Aliceは何人いる?」や「バイバイ、Alice」でforループを使って出力していた部分も、ListArrayですが同じようにできるのでしょうか?
実験実験。
import java.util.*; public class ArrayListTest4 { public static void main(String[] args) { ArrayList<String> strList = new ArrayList<String>(); strList.add("Alice"); strList.add("Bob"); strList.add("Chris"); ArrayList<Integer> intList = new ArrayList<Integer>(); intList.add(1); intList.add(2); intList.add(3); System.out.println("ArrayList<String> : " + strList); System.out.println("ArrayList<Integer> : " + intList); } }
E:\study\19>java ArrayListTest4 ArrayList<String> : [Alice, Bob, Chris] ArrayList<Integer> : [1, 2, 3]
できますね。toString、素晴らしいなぁ。
バイバイ、Alice。
「Aliceは何人いる?」ですが、よく考えたら保留するほどのことでもないですね。きっと、removeの引数がStringの"Alice"であれば"Alice"が消えて、intの5であれば5番目が消えるのでしょう。やってみます。
// list.remove("Alice"); list.remove(5);
E:\study\19>java ArrayListTest add Alice and 4 : Alice Bob Chris Diana Elmo add another Alice : Alice Bob Chris Diana Elmo Alice remove Alice : Alice Bob Chris Diana Elmo
「じゃあリストの中身がintの時は?」と思ったのだけど、これも簡単。参照型ではないintのArrayListは作れなくて、代わりにラッパーのIntegerを指定するように、とのことなのだから、Integerオブジェクトを与えればその値が消えて、int値を与えればそのインデックスの番号が消えるのでしょう。やってみます。
list.remove(6);
list.remove(new Integer(6));
E:\study\19>java ArrayListTest3 add 3, 6, 9, 6, 12, 6, 15 : 3 6 9 6 12 6 15 remove 6 : 3 6 9 6 12 6 remove Integer(6) : 3 9 6 12 6
「remove 6」とした際にはインデックスが6の値、つまり最後にあった「15」が消えています。次に「remove Integer(6)」とした時には、最初に出てくる、インデックスでは1の位置にあった「6」が消えています。
Aliceは何人いる?
ArrayListからの削除と確認の項に、次のようなコードがあります。
// AliceとBobとElmoを削除 list.remove("Alice");
Aliceが二人いたら、一人だけ消えるのでしょうか?それとも二人とも消えるのでしょうか?一人だけの時は、どちらが消えるのでしょうか?
ということで、例によって実験。
import java.util.*; public class ArrayListTest { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); System.out.print("add Alice and 4 : "); list.add("Alice"); list.add("Bob"); list.add("Chris"); list.add("Diana"); list.add("Elmo"); for (int i = 0; i < list.size(); i++) System.out.print(list.get(i) + " "); System.out.println(""); System.out.print("add another Alice : "); list.add("Alice"); for (int i = 0; i < list.size(); i++) System.out.print(list.get(i) + " "); System.out.println(""); System.out.print("remove Alice : "); list.remove("Alice"); for (int i = 0; i < list.size(); i++) System.out.print(list.get(i) + " "); System.out.println(""); } }
E:\study\19>javac ArrayListTest.java E:\study\19>java ArrayListTest add Alice and 4 : Alice Bob Chris Diana Elmo add another Alice : Alice Bob Chris Diana Elmo Alice remove Alice : Bob Chris Diana Elmo Alice
最初に見つかったAliceにバイバイするみたいですね。他のAliceは、まだ残っていました。
じゃあ、2番目のAlice、あるいは最後のAliceにバイバイするにはどうすれば良いでしょう。ここでは、ひとまず保留します。
文字コードの変換をしながら読み込み
ファイルから"EUC-JP"の文字データを読み込むと、文字が化けているように思われたので、対応方法を調査。まず、以下を手がかりにしました。
文字ストリームを扱うReaderやWriterを使えば、入出力を行うと同時に、文字コードの変換も行うことができるのです。
文字ストリームを扱うReaderとして、InputStreamReaderが挙げられているので、これとFileReaderを組み合わせることを考えました。InputStreamReaderのドキュメントを見てみると...「直系の既知のサブクラス:FileReader」となってます。じゃあなぜ?
しかしよく考えてみると、内部ではUnicodeで処理されるJavaですが、MakeHtmlなどはシフトJISのデータ・ファイルを読ませているのにちゃんと処理されています。そこで、今度は次の部分を手がかりに。
InputStreamReader reader = new InputStreamReader(stream, charset); ここで、streamはバイトストリームであり、charsetは"Shift_JIS"や"EUC-JP"など文字のエンコードを指定する文字列です。charsetを省略すると、プラットホーム依存(ネイティブ)のエンコード方法になります。
シフトJISはネイティブ・エンコードだから変換が働くけど、EUC-JPはそうではないからうまくいかない、と仮定。まずFileReaderはInputStreamReaderからgetEncodingというメソッドを継承しているとのことなので、実行してみます。
import java.io.*; public class TellEncode { public static void main(String[] args) throws FileNotFoundException, IOException { FileReader reader = new FileReader("TellEncode.java"); System.out.println("現在のエンコーディングは " + reader.getEncoding() + " です。"); reader.close(); } }
E:\STUDY\18>java TellEncode 現在のエンコーディングは windows-31j です。
windows-31jはShift_JISを拡張した「Windows-31J」と呼ばれるWindows標準文字セット(@IT)ですね。ここまではOKとして、次にエンコーディングの指定。
FileReaderのドキュメントを見るとcharsetを指定できるコンストラクタの記法も、charset指定用のメソッドも見当たりません。できないはずはないだろうという思いと、FileReaderはInputStreamReaderのサブクラスなんだから、ということで、InputStreamReaderでの指定方法を真似てみました。
E:\STUDY\18>javac MakeHtml2.java MakeHtml2.java:14: シンボルを解決できません。 シンボル: コンストラクタ FileReader (java.lang.String,java.lang.String) 場所 : java.io.FileReader の クラス BufferedReader reader = new BufferedReader(new FileReader(sourcefile, "EUC-JP")); ^
だめだそうです。この方向はあきらめて、ファイルからの入力をInputStreamReaderにかけることを検討。再度InputStreamReaderのドキュメントを開いて、コンストラクタを確認します。
InputStreamReader(InputStream in) InputStreamReader(InputStream in, Charset cs) InputStreamReader(InputStream in, CharsetDecoder dec) InputStreamReader(InputStream in, String charsetName)
いずれにしても、引数にFileReaderは使えず、InputStreamクラスじゃなきゃいけない様子。今度はInputStreamのドキュメントを開くと、「直系の既知のサブクラス」に「FileInputStream」というのがありますね。これかな?
FileInputStreamのコンストラクタを確認し、真似てみます。
// BufferedReader reader = new BufferedReader(new FileReader(sourcefile)); // BufferedReader reader = new BufferedReader(new FileReader(sourcefile, "EUC-JP")); BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourcefile), "EUC-JP"));
E:\STUDY\18>javac MakeHtml2.java E:\STUDY\18>java MakeHtml2 MakeHtml.txt MakeHtml.htm E:\STUDY\18>
コンパイルも通って、実行結果もOK。EUCコードのMakeHtml.txtを処理させましたが、MakeHtml.htmにはちゃんとHTMLへの変換が済んだ状態で、シフトJIS(というより、おそらくwindows-31Jなのでしょうね)で出力されています。
何とか調べられましたが、ドキュメントを調べるって厄介です。それに、本当にこのやり方が妥当だったのか、ということは疑問として残ります。