Pythonでreduce(l|r) [Main] コーディング過程をLingrで中継

[Java(1)]

Javaでリストとかfor文とか

どうも、Java勉強中の西尾です。 Javaって難しいですね…。 今日はようやくリストを作る方法を理解したので、 自分の勉強のためにまとめてみたいと思います。 普段Pythonの記事ばっかりなのでたまには気分を変えて。

Pythonでは「複数のオブジェクトが入った配列のようなオブジェクト」を下のような記法で作成できます。

>>> xs = [1, 2, 3.14, "Hello!"] 
JavaScriptやRubyでも同じで、Perlだと@xs = (1, 2, 3.14, "Hello")になるようです。

また、このオブジェクトの中身を順に表示するにはfor文を使います。

>>> for x in xs:
        print x

        
1
2
3.14
Hello! 

これと同じことをJavaではどう書けばいいのか、 今日やっと理解しました。 Javaでは下のようになります。

Object[] xs = { 1, 2, 3.14, "Hello!" };
for (Object x : xs) {
        System.out.println(x);
}
ただ、これではxsが配列なので、 Pythonでは下のように書ける「アイテムの追加」などができません。
>>> xs = [1, 2, 3.14, "Hello!"]
>>> xs.append("Java")
>>> xs
[1, 2, 3.1400000000000001, 'Hello!', 'Java']
アイテムの追加などが可能なオブジェクトにするために、 Collectionインターフェイスを実装したクラス (Collection (Java 2 Platform SE 5.0)) のオブジェクトを作りたいと思います。 それにはArrays.asListを使います。
Vector<Object> xs = new Vector<Object>(Arrays.<Object>asList(1, 2, 3.14, "Hello!"));
Arrays.asListに配列を渡すと、List(これはCollectionの子インターフェイス)を 実装したクラス(Arrays$ArrayList)のオブジェクトを返してくれます。 なので下のように配列を渡しても上と同じことができます。
Arrays.asList(new Object[]{ 1, 2, 3.14, "Hello!"})
可変長引数でも渡せるので、実際に配列を作る必要はなく、 配列を作るための呪文「new Object[]{~}」が省けます。 ただしその場合「これらがObject型である」という情報が失われてしまうので、 それを補うために「Arrays.<Object>asList」と指定しています。

さて、これでListを作るところまではできたのですが、 残念ながらここで作られるListのaddメソッドを呼ぶと、 「サポートされていない」という趣旨の例外が発生します。 (UnsupportedOperationException)

おそらく、配列をListに詰め替えているのではなく、 単にListに見えるラッパをかぶせているだけなのでしょう。 そこでVectorに詰め替えます。 VectorのコンストラクタにCollectionを渡した場合には、 中身の詰め替えが行われます。 正確に言うならば、仕様にはそういうことは書かれていない (どころか、返ってくるリストがArraysのprivateなインナークラスなのでそもそも以下略) のでソースコードを追うことになります。 Vectorのコンストラクタで、渡されたCollectionのtoArrayを呼ぶのですが、 Arrays$ArrayListのtoArrayは自分の持っている配列をcloneして返すので、 この時点で詰め替えが行われます。

さてこれで追加や削除ができるようになりました。 ここでクイズです。下のコードを実行するとどういう出力が出るでしょうか?

Vector<Object> xs = new Vector<Object>(Arrays.<Object>asList(1, 2, 3.14, "Hello!"));
xs.remove(3.14);
xs.remove(1);
xs.set(1, "newItem");
xs.add(1, 999);
xs.add("Java");
Collections.reverse(xs);
for (Object x : xs) {
        System.out.println(x);
}

ヒント、Pythonで書くとこうなります。

>>> xs = [1, 2, 3.14, "Hello!"]
>>> xs.remove(3.14)
>>> xs.pop(1)
2
>>> xs[1] = "newItem"
>>> xs.insert(1, 999)
>>> xs.append("Java")
>>> xs.reverse()
>>> for x in xs:
	print x

解答。

Java
newItem
999
1
出力結果に「1」が入っているのが意外です。 実はxs.remove(3.14);とxs.remove(1);ではremoveの意味が違い、 前者は「3.14という値を取り除く」のに対し、後者は「1番目の位置の値を取り除く」 になっています。 いやはや、Javaは奥が深いですね。

追記: なぜこういう現象が起きるのかについて解説します。remove(int)とremove(Object)の両方のメソッドがあって、リテラルの1は(過去の諸々のしがらみによって)int型なのでremove(1)はremove(int)が呼ばます。一方リテラルの3.14はオートボクシングでObjectになるのでremove(Object)の側が呼ばれます。もしremove(Object)で1を削除したければ 明示的にxs.remove(new Integer(1));と書く必要があります。

ご指摘どうもありがとうございます>Yのほぼコード置場 - Re:Javaでリストとか(ry