Javaエバンジェリストがお届けするホットな話題
5分でわかる今週のJava ホットトピック
日曜日 1 20, 2008
今週のJavaクイズ(8):先人の教えを整理すると・・・?

Java Puzzler、続いての出題です!

先人の教えを整理すると・・・?

では、まず問題のコードを見てみましょう。

http://blogs.sun.com/javaev/resource/mini-WS000095.jpg

登場するクラスは String クラスだけ、という簡単(そう) なコードですね。
"Less is more." "Time flies." "Money talks." という三つの文字列を連結し、String クラスの replaceAll() メソッドで "." を "," に置き換えようというプログラムです。

さて、このプログラムの結果は以下のどれでしょうか?

 

ちなみに "Less is more." は「過ぎたるは及ばざるがごとし」、"Time flies."は「光陰矢のごとし」 "Money talks." は「金が物を言う」ということわざです。日本にも外国にも似たような意味のことわざがあったりして、比較するとおもしろいですね。「覆水盆に返らず」と "It is no use crying over spilt milk." は同じ意味ですが、文化の違いがよく出ているなぁ、と感心してしまいます。

それでは、答えはまた次回!


月曜日 1 07, 2008
今週のクイズ(7) 解答編:何かと話題のBoxing・・・結果は?

去年出題した Autoboxing の問題の解答を掲載します! (長い間お待たせしてしまってすみません・・・)
まずは問題の復習から。

public class Boxing {
static int i;
static Integer integer;
public static void main(String[] args) {
System.out.println(integer == i);
}
}

というようなコードでした。さて、このプログラムの結果は以下のどれでしょうか?

  • 1) true
  • 2) false
  • 3) 実行時例外が発生する
  • 4) コンパイルエラーになる

という問題でした。わかりましたか?

それでは、解答をみていきましょう。
正解は・・



3 の「実行時例外が発生する」でした。

どうしてエラーになってしまうのでしょう?
integer == i の計算式は、実行時に integer.intValue() == i というように変換されます。そうすると、integer は明示的に初期化されていないので、ポインタは null となっています。そのため、NullPointerException が発生してしまうという結果になってしまいました。

http://blogs.sun.com/javaev/resource/mini-boxing_answer1.jpg

では、正しく実行させるためにはどのようなコードを書けばいいのかというと・・・
次のようなコードです。

integer != null && integer == i

このように書くと、integer が null でない場合だけ、式が評価されるようになりますね。

http://blogs.sun.com/javaev/resource/mini-boxing_answer2.jpg
ということで、今回のポイントです。
AutoBoxing が使えるからといって、基本データ型と同じような感覚で使っていると、思わぬエラーが発生するということですね。

http://blogs.sun.com/javaev/resource/mini-boxing_answer3.jpg
それでは、また次回。


火曜日 11 20, 2007
今週のJavaクイズ(7):何かと話題のBoxing・・・結果は?

おまたせいたしました! Sun Tech Days で大好評だった Java Puzzler の問題を少しずつ載せていきたいと思います。

何かと話題のBoxing・・・結果は? 

public class Boxing {
static int i;
static Integer integer;
public static void main(String[] args) {
System.out.println(integer == i);
}
}


J2SE 5.0 からの新機能、Autoboxing の問題です。int 型の i と Integer 型の integer、それぞれの変数を初期化しないまま比較していますが・・
さて、このプログラムの結果は以下のどれでしょうか?

  • 1) true
  • 2) false
  • 3) 実行時例外が発生する
  • 4) コンパイルエラーになる

Autoboxing って何だっけ?という方は、以下で復習しておきましょう!

オートボクシング / アンボクシング機能


木曜日 4 05, 2007
今週のJavaクイズ(6):ボタンを押すとどうなる?

今回はJava in the Boxでおなじみの櫻庭さんからの出題です。

ボタンを押すとどうなる?

次のプログラムを実行して、表示されるボタンを押して、なが〜い処理をしている間、ウインドウの表示はどのようになるでしょうか?


public class Monologue implements ActionListener {
private JButton button = new JButton("実行");

public Monologue() {
JFrame frame = new JFrame("Monologue");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button.addActionListener(this);
button.setPreferredSize(new Dimension(100, 26));

frame.add(button);
frame.setSize(100, 70);
frame.setLayout(new FlowLayout(FlowLayout.CENTER));
frame.setVisible(true);
}

public void actionPerformed(ActionEvent event) {
button.setText("処理中...");
button.setEnabled(false);

// なが〜い処理 ^^;;

button.setText("実行");
button.setEnabled(true);
}

public static void main(String[] args) {
new Monologue();
}
}

そのときウインドウの表示がどのようになっているかを次の中からお選びください。

puzzler

今週のクイズ(5) 解答編:顔文字

更新が遅れてしまい誠に申し訳ありませんでした m(._.)m なるべく定期的な更新を継続のための努力をしていきますが、しばらくの間は更新が不定期になるかもしれません・・・。さて、解答編のご紹介がおくれてしまった先々週のクイズの解答をご紹介しておきます。まずは問題の復習からです。

顔文字

次のプログラムのうち、どのようにメソッドや変数を定義してもエラーとなってしまうのはどのケースでしょうか?

switch (face) {
case 1:
while (ToT)
;
case 2:
while (ミ^v^ミ)
;
case 3:
for (;_;)
;
case 4:
while (つд⊂)
;
case 5:
ヾ(-_ー)
;
case 6:
while (x_x)
;
}

解答

解答はこのプログラムを統合開発環境で表示してみると一目瞭然ですね。

Java Quiz Answer

ご覧の通り、答えは4番のようですね。では一つずつ解説していきます。まず、一つ目の

while (ToT)

ですが、これは簡単ですね。boolean ToT = true; のような変数をあらかじめ定義しておけばプログラムは問題なく実行されます。T (大文字のティー)も o (小文字のオー)もちゃんとJavaで変数名で利用する事ができるからです。さて次は

while (ミ^v^ミ)

です。これはちょっとだめなんじゃ??と、一瞬思うかもしれません。問題を考えるときも一番苦労しました(^^; この選択肢のポイントは次の二つです。

  • Javaの変数名にはカタカナも使える
  •  ^ は、排他的論理和

ということです。実は、顔と思っていた部分も分解すると、カタカナの「ミ」という名前の変数と、アルファベット小文字の「v」の排他的論理和の式だった訳です。なのでこれも、boolean ミ = true, v = true; などと事前に定義しておけば無事実行する事ができます。

では3番目を見てみましょう。

for (;_;)

これはわりと有名な無限ループの書き方ですね。for文は (開始; 継続条件; 増分) のように記述しますが、開始と増分はこの中に書かなくても良いことになっています。このケースは継続条件のところに 「_」(アンダーバー) という変数を置いて for文を成立させています。アンダーバーもJavaの変数名として有効な文字ですからこれも、boolean _ = true; などと事前に宣言しておけば問題ありません。さて次はエラーとなってしまうケースです。

while (つд⊂)

さて、これはなぜエラーとなってしまうのでしょうか。その前にまず1文字ずつ見てみましょう。1文字目はひらがなの「つ」です。先ほど、カタカナがokだったので同様にひらがなも問題ないのは想像に難しくありません。そのつぎの「д」はキリル文字で、アルファベットでいうと D 相当の文字です(参照: Wikipedia)。Javaでは文字は基本的にユニコードで処理されており、国際化されているのでこのようなキリル文字も扱うことができるわけです。

では、それならばなぜ「⊂」はダメなのでしょうか。実は「⊂」は、数学で包含関係を表す記号です。Javaではこのような記号に分類される文字を変数名やメソッド名等の識別子として使う事ができません。このため、どのように変数を事前に定義しておいても「⊂」は不正な文字としてコンパイラによってエラーとされてしまいます。ちなみに、Javaで識別子として利用できる文字はjava.lang.Character#isJavaIdentifierStartメソッド(開始の1文字目)と、java.lang.Character#isJavaIdentifierPartメソッドをつかって調べる事ができます。

さて、ここまでわかればあとの二つは簡単ですね。

ヾ(-_ー)

これは、 「ヾ」という濁音付きカタカナの繰り返し記号(KATAKANA VOICED ITERATION MARK)で、これもカタカナと同様Javaの識別子として利用できる文字です。なので、void ヾ(int a) {} のようなメソッドを用意しておけば良い訳です。ちなみに、引数の 「-_ー」は、「-」(マイナス)、「_」(アンダーバー)、「ー」(長音記号)です。アンダーバーも、長音記号も変数名として使えます。マイナスは変数名にはできませんが、後続の「_ー」が数字であれば負の数を表すためのマイナスとなるので、これも問題ない訳です。

ちょっとこの問題の微妙だったところは長音記号のところです。これが実は見た目が同じ「—」(全角ダッシュ)の場合には記号と見なされコンパイルする事ができません。

Java Quiz Answer

さて、最後の

while (x_x)

は簡単ですね。小文字エックス、アンダーバーの組み合わせはJava識別子として有効なので boolean x_x = true; という変数を定義すれば良い訳です。


水曜日 3 14, 2007
今週のクイズ(5):顔文字

今回はちょっと変り種のクイズです。

顔文字

次のプログラムのうち、どのようにメソッドや変数を定義してもエラーとなってしまうのはどのケースでしょうか?

switch (face) {
case 1:
while (ToT)
;
case 2:
while (ミ^v^ミ)
;
case 3:
for (;_;)
;
case 4:
while (つд⊂)
;
case 5:
ヾ(-_ー)
;
case 6:
while (x_x)
;
}

水曜日 3 07, 2007
今週のクイズ(4) Again!:シャッフル

前回のクイズ シャッフル、ですが、今気づけば肝心の問題の部分が何か、が良くわからない状態になっていました。大変失礼いたしました。もう一度問題をご紹介します。

シャッフル/シャッフル/シャッフル

public class Shuffle {
public static <T> void shuffle(T[] a) {
Random r = new Random();
for (int i = 0; i < a.length; i++) {
int j = r.nextInt(a.length);
int k = r.nextInt(a.length);

swap(a, j, k);
}
}
public static <T> void swap(T[] a, int i, int j) {
if (i != j) {
T tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
}

こ のプログラムは配列の内容をランダムに再配置するというプログラムです。ためしに Integer[] a = {1, 2, 3} という配列をこのプログラムを使って6,000回シャッフルし、その組み合わせの出現数を数えてみると次のようになりました。

[1, 2, 3]=1129, [1, 3, 2]=1029, [2, 1, 3]=1021, [2, 3, 1]=853, [3, 1, 2]=886, [3, 2, 1]=1082}

さて、この結果から判断できるのは次の4つのうちどれでしょうか。

  • (1) 乱数なのでこの程度の偏りは偶然。もう一度実行すると別のパターンに偏る。
  • (2) java.util.Random の乱数は周期性があるので、特定のパターンに偏りが出る。
  • (3) java.util.Random の代わりに java.security.SecureRandom を使うと偏りは小さくなる。
  • (4) このアルゴリズムでは常に特定のパターンに偏る。 

水曜日 2 28, 2007
今週のクイズ(4):シャッフル

先週はJSPの問題でしたが今回はまたJava SEな感じの問題です。

シャッフル/シャッフル/シャッフル

 安全なパスワードを作りたい、音楽の再生順序を十分にランダムな順んにしたい、写真のスライドの順番をランダムにしたい、などなど、ランダムに並び替えたい、という事情は実はかなり身近な問題です。今回はそんなプログラムです。なお、今回のプログラムでは、完全にこれが答え!というのはなく、簡単な改造で十分ランダムになるという程度しか回答は用意できていませんがご了承ください。

public class Shuffle {
public static <T> void shuffle(T[] a) {
Random r = new Random();
for (int i = 0; i < a.length; i++) {
int j = r.nextInt(a.length);
int k = r.nextInt(a.length);

swap(a, j, k);
}
}
public static <T> void swap(T[] a, int i, int j) {
if (i != j) {
T tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
}

今週のJavaクイズ(3) 回答編:ある日の為替レート
さて今回の問題はいかがだったでしょうか? すでにJSPで類似のものを実装されていて経験されている方には簡単だったと思います。

ある日の為替レート

表示されるレートはそれぞれいくらになるでしょうか? その理由もお考えください。

<%@ page contentType="text/html" pageEncoding="utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<html>
<body>
米ドル: <fmt:formatNumber pattern="0.00" value="116.265000"/>円 <br />
豪ドル: <fmt:formatNumber pattern="0.00" value="90.584330"/>円 <br />
英ポンド: <fmt:formatNumber pattern="0.00" value="226.01753"/>円
</body>
</html>

回答

今回の問題のポイントはformatNumberによって小数点以下がどのように扱われるかでした。まず、これを実行した結果を見てみましょう。

米ドル: 116.26円
豪ドル: 90.58円
英ポンド: 226.02円

この結果から考えると小数点以下はそれぞれ次のように処理されたように見えます。

  • 米ドルは切り捨て
  • 豪ドルも切り捨てか四捨五入
  • 英ポンドは切り上げか四捨五入

どうして同じフォーマットのパターン 0.00 を使っているのにこのように処理が変わってしまうのでしょうか。これは、formatNumberタグが利用しているフォーマッタが、java.text.DecimalFormatで、小数点以下の処理方法がROUND_HALF_EVENだからです。ROUND_HALF_EVENは仕様によると

 「もっとも近い数字」 に丸めるモードです。ただし、両隣りの数字が等距離の場合は偶数側に丸めます。破棄する小数部の左辺の桁が奇数の場合は ROUND_HALF_UP のように動作し、偶数の場合は ROUND_HALF_DOWN のように動作します。この丸めモードは、連続する計算で繰り返し適用される場合に累積エラーを最小限にします。

 というように、少数部の左辺の桁が奇数なのか、偶数なのかによってそれぞれ動作が異なるため、今回の問題のように結果が分かれた訳です。
 


水曜日 2 21, 2007
今週のJavaクイズ(3):ある日の為替レート

今回はJSPの問題です。

ある日の為替レート

表示されるレートはそれぞれいくらになるでしょうか? その理由もお考えください。

<%@ page contentType="text/html" pageEncoding="utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<html>
<body>
米ドル: <fmt:formatNumber pattern="0.00" value="116.265000"/>円 <br />
豪ドル: <fmt:formatNumber pattern="0.00" value="90.584330"/>円 <br />
英ポンド: <fmt:formatNumber pattern="0.00" value="226.01753"/>円
</body>
</html>

 


先週のJavaクイズ(2) 回答編:マルチスレッド環境にて

お待たせしました、少し難しめの問題でしたがいかがだったでしょうか?今回の問題は先月の「今月のJava Hot Topicセミナー(1月号)」で山口さんによる「コンカレンシー・ユーティリティのすすめ」をご覧頂くとその問題点と解決策がおわかりいただけると思います。

ではまず問題の復習から。

このプログラムのバグを取り除いてください

 次のプログラムはupdate(int)メソッドによって今までの最大値と比較して、与えられた数が今までで最大であれば、最大値を更新するようなプログ ラムです。しかし、このプログラムはある状況下で期待通り動作しません。そのバグの内容と、バグを修正したプログラムを考えてください。

 

public class MaxValue { 
private int max;

public void update(int value) {
if (max < value)
max = value;
}

public int get() {
return max;
}
}

 

回答

この問題の解答は幾つかパターンが考えられますが、今回は上記でご紹介したセミナーの資料「コンカレンシー・ユーティリティのすすめ (以下、「資料」と言うときはこの資料のことです)」にそって回答をご紹介します。

このプログラムは一見難の問題も無く動作しそうですが、実はマルチスレッド環境では正しく動作しないことがあります。これはJavaのメモリモデルと深く関わる問題です。
資料の p8〜p9ご覧ください。近代的なコンピュータはデータをCPUのレジスタとメモリだけでなく、処理を高速化させるために様々な部分にキャッシュをするようになっています。それもCPUだけでも二次キャッシュ、三次キャッシュというように多段の構造になっている事も珍しい事ではありません。

20070131_hottopic2_concurrency_p8

このような場合に、CPU 1でこれから処理を仕様としているときに、CPU 2で今まさに更新されたデータを利用するとするとどうなるでしょうか。このとき、CPU 1はデータが更新された事を知らないので、古い情報を使ってしまう場合があります。

これに対処するにはJ2SE 5.0で厳密に定義されたメモリモデルのルールに従ったプログラミングをする必要があります。

20070131_hottopic2_concurrency_p9

これをふまえてプログラムを修正すると次の2パターンのようになります。

パターン1
20070131_hottopic2_concurrency_p12
パターン2
20070131_hottopic2_concurrency_p13

パターン1ではすべてのメソッドを同期化することで安全に呼び出しを行っています。一方、パターン2ではvolatileをmaxを宣言する際に装飾する事で必ずこの変数を読み出す前にキャッシュをクリアし、書き出す時にキャッシュをフラッシュするようにしています。パターン2の方は読み出しについては同期化されないためパフォーマンスのオーバーヘッドが少なくてすみます。

これで十分な場合もほとんどですが、さらにパフォーマンスのボトルネックを減らす方法を考えます。近代的なマルチCPUシステムでは、CPUあるいはそのシステムによってメモリの整合性を保証しつつ値を更新するような命令がサポートされています。その典型的なものがCAS (Compare And Swap)命令です。CAS命令は

CAS v, a, b

のように3つの引数を持つ命令で、v == aなら vに bを代入するという命令です。この操作はCPUあるいはシステムによって整合性が保証されるため、この操作を行うためにロックを取得したり、ロック待ちになるということが無く、(アプリケーションやOSから見て) 処理の流れを止める事がありません。このCAS命令は J2SE 5.0から導入された java.util.concurrent パッケージのユーティリティを使う事で Javaからも利用可能になりました。この concurrentパッケージを使った回答例を見てみましょう。

パターン3
20070131_hottopic2_concurrency_p20

まずCAS命令を利用するためにjava.util.concurrent.AtomicIntegerクラスを使って最大値 maxを保持しています。そして、updateメソッドでは compareAndSwap (つまり CAS)を使って値を更新しているのです。この compareAndSwapは max == oldの場合には実行が成功して戻り値が trueになりますが、max != oldつまり、別のスレッドによって値が更新された場合には戻り値が falseとなって再度比較を行うようにしています。

このようにしてCPUやマルチプロセッサ/マルチコアシステムで提供されている強力な命令を利用したプログラムに生まれ変わりました。皆さんもこれを参考にしてスループット重視の安全なプログラムを java.util.concurrentパッケージを使って実装してみてください。


水曜日 2 14, 2007
今週のJavaクイズ(2):マルチスレッド環境にて

ほんの2〜3年前までは複数のCPUあるいはコアを搭載したコンピュータといえば企業や研究施設以外で目にする事はほとんどありませんでした。しかし、昨今のトレンドではパーソナル向けのコンピュータでも複数コア/複数CPUを搭載する物が珍しくなくなってきました。このような状況になるに従って、今まではほとんど見た事もなかったようなバグが発見される事も珍しくなくなってきました。今回はそんな状況に関する問題です。

このプログラムのバグを取り除いてください

 次のプログラムはupdate(int)メソッドによって今までの最大値と比較して、与えられた数が今までで最大であれば、最大値を更新するようなプログラムです。しかし、このプログラムはある状況下で期待通り動作しません。そのバグの内容と、バグを修正したプログラムを考えてください。

public class MaxValue { 
private int max;

public void update(int value) {
if (max < value)
max = value;
}

public int get() {
return max;
}
}

先週のJavaクイズ(1) 回答編:何が印刷されるでしょうか?
ではお待ちかねの回答を紹介します。まずは問題とプログラムをおさらいしておきましょう。

何が印刷されるでしょうか?

次のプログラムを実行するとなにが印刷されるでしょうか?  答えとその理由をお考えください。

import java.util.*;

public class Equality {
public static void main(String args[]) {
Map<Short, Short> map = new HashMap<Short, Short>();
for (Short i = 0; i < 365; i++)
map.put(i, i);

for (Short i = 0; i < 365; i++) {
if (map.get(i) != i) {
System.out.println(i);
System.exit(0);
}
}
}
}

回答

答えは128です。なぜ?と思われた方も多いかもしれません。まず、一番直感的に答えを考えるならば「何も出力されない」が答えになると推測できます。しかしよく注目してください。ここで、i や mapの中に格納されている変数は java.lang.Short 型であり、 != や == で比較する場合には値ではなく参照を比較していることになります。つまり、この比較は本来は

!map.get(i).equals(i)

のように書かなければならなかったのです。ではそれをふまえて考えると、答えは「0」であるという風に判断できます。しかしこれも実は違うのです。ここから先は通常知らなくともあまり差し障りは無いのですが、すこしマニアックな引っかけ問題になっていました。

Java言語仕様第3版 5.1.7 Boxing Conversionによると

If the value p being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.

このようにJava言語仕様によると、-128から127の間の数字に関しては常に参照も等しいようにすることが決められています。ですから、127を超える最初の数字 128 の参照は等しくならず、印刷されるのは 128 となります。


水曜日 2 07, 2007
今週のJavaクイズ(1):何が印刷されるでしょうか?

Javaエバンジェリストグループでは「2時間で学ぶ今月のJavaホットトピック」と題して毎月無料セミナーを夕方に開催しています。このセミナーでは、だいたい2つ程度のトピックほどのプレゼンテーションの他に、参加いただいた方には大変好評なJavaを使った問題を出題しています。このブログでは過去のセミナーでご紹介した問題を毎週ご紹介していきます。なお、答えは次の週までお楽しみ。

何が印刷されるでしょうか?

次のプログラムを実行するとなにが印刷されるでしょうか?  答えとその理由をお考えください。

import java.util.*;

public class Equality {
public static void main(String args[]) {
Map<Short, Short> map = new HashMap<Short, Short>();
for (Short i = 0; i < 365; i++)
map.put(i, i);

for (Short i = 0; i < 365; i++) {
if (map.get(i) != i) {
System.out.println(i);
System.exit(0);
}
}
}
}

 


過去の記事
« 11月 2009
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
     
       
今日
Click me to subscribeこのブログを購読する(RSS)
検索

リンク
 

Today's Page Hits: 14