Java技術者面接質問まとめ(2)

「等値」と「等価」の違いを説明してください。

等値とは、変数が指すメモリ域の Bit 配列が等しいこと。等価とは、インスタンスが持つ情報が意味的に等しいこと。

Java 言語を例に挙げると、

Integer a = new Integer(0);
Integer b = new Integer(0);
assert a != b; // 異なるインスタンスへの参照であるため、等値ではない
assert a.equals(b); // インスタンスが持つ情報は両方とも整数の 0 なので、等価

「値渡し」と「参照渡し」の違いは何ですか?オブジェクト指向システムや手続き型システムにおいて、これらにはどのような違いが存在するのかを説明してください。

それらは、何らかのデータを異なる処理領域に渡すときの方式を表す。例えば、関数に対して、関数の外から引数でデータを渡すとき。
値渡しは、データそのものをコピーして渡す。
参照渡しは、データへアクセスするための情報を渡す。例えばポインタ等。

値渡しがデータサイズが大きいほど遅くなるのに対し、参照渡しは一定で、一般的に高速。
値渡しは渡した先でデータが書き換えられても、元のデータには影響しない。他方、参照渡しは、渡した先でデータが書き換えられたら、元のデータが書き換わってしまう。
つまり、一般的に、値渡しは低速だが安全で、 参照渡しは高速だが危険だ。

C/C++ 言語では、const 修飾子によって安全に参照渡しができる (抜け道があるので、真実安全ではないが)。
Java 等の最近のオブジェクト指向言語では、オブジェクトは参照渡ししかできない場合が多い上に、const 修飾子のような機能はない。 そのため、防御的コピーという手法を用いて安全性を確保する。これは値渡しよりもさらに低速である。

「ポリモーフィズム」とは何かを説明してください。

複数の異なる実装を持つことができるインターフェイスの性質。

例えば、仮想関数を持つクラスはポリモーフィズムを備えている。派生クラスによって、仮想関数に異なる実装を与えることができる。
例えば、Ruby などの動的型付け言語の変数はポリモーフィズムを備えている。変数に格納されたオブジェクトによって、同じシグネチャを持つメソッドに異なる実装を与えることができる。
例えば、C++テンプレートで指定された型の変数はポリモーフィズムを備えている。指定された型によって、同じシグネチャを持つメンバ関数に異なる実装を与えることができる。

「悲観的ロック」と「楽観的ロック」を比較し、違いを明確に述べてください。

悲観的ロックとは、データ操作を行う前に、操作が成功するために十分な手を打っておくこと。いわゆる排他ロック。
楽観的ロックとは、とりあえずデータ操作を行ってから検証し、失敗してたらやり直すこと。いわゆる Lock-Free アルゴリズム。

CPU のクロック数が限界を迎え、コアを増やす方向に発展を続ける昨今は、並列処理を適切に行わなければ性能を維持できない。
悲観的ロックを行ってしまうと、データ操作を並列処理できない。
楽観的ロックでは、頻繁にデータ操作がバッティングしないことを前提に、効率よく並列処理が行える。

楽観的ロックは、CAS 命令を活用して実現する。
Java では ConcurrentHashMap, C# では ConcurrentDictionary 等、高度な楽観的ロックのアルゴリズムを備えたコンテナが標準で提供されている。

数値Xのフィボナッチ数を計算するアルゴリズムを示す。

フィボナッチ数の意味を知らないため、無理 orz

指定された数値Xが素数であるかどうかを判定するアルゴリズムを示す。

とりあえず、X未満2以上のすべての正数で割れなきゃ素数よね?

if (X <= 0) return false;
if (X <= 3) return true;
for (int i = 2; i < X; ++i) {
if (X % i == 0) return false;
}
return true;

効率悪すぎるわっ!
X以下の素数を計算する単純な数式があったと思うけど、覚えていない。 それで計算して入ってれば素数、とか。

Collection factors = calculateFactorsLowerThan(X+1);
return factors.contains(X);

うわー・・・

ループを使わずに配列の順序を逆にするアルゴリズムを示す。

ループって、for, for-each, while, do-while ?

えーと、再帰すればいいのかな。以下 C++ 言語

template
void reverseArray(T* begin, T* end) {
if (begin >= end) return;
std::swap(*begin, *(end-1));
reverseArray(begin+1, end-1);
}

型 T はコピーできることが条件。

Fizz Buzz

これ、別件でこないだやった。X 未満の数までの Fizz Buzz

for (int i = 1; i < X; ++i) System.out.println(i % 3 == 0 ? (i % 5 == 0 ? “Fizz Buzz” : “Fizz”) : (i % 5 == 0 ? “Buzz” : i));
えー。
答え合わせはしていないので、とんでもない間違いがあるかもしれません。