2009年02月25日

プログラマー面接時の技術的な質問事項(アプレッソ版) - 解答編

Check このエントリーをはてなブックマークに追加
昨日、プログラマー面接時の技術的な質問事項(アプレッソ版)を書いたところ、「自分ならこう答える」というエントリを書いてくれた人が何人かいて、個別にコメントしようかとも思ったのだが、昨日のエントリだけだと質問の投げっぱなしになってしまうところもあるので、解答編を書くことにした。

なお、「面接の質問項目を公表しちゃっていいの?」という指摘もあったが、ブログに書いたのはあくまでも質問項目の一例だし、解法を検討する過程を見れば普段どんな風に開発しているのかはだいたいわかるので、特に問題ない。

■ プログラミング基礎編

  • for (int i = 0; i < list.getLength(); i++) {}の潜在的パフォーマンスボトルネック
    list.getLength()がlist.getLength()回評価されてしまう。具体例としては、JREに標準で付属するDOMのライブラリのNodeListの実装はlist.getLength()内で非常に重い処理をしているため、これが原因で劇的にパフォーマンスが劣化することがある。
    (まあ「getLength()」で重い処理するなよ、という話もありますが)

    この問題を回避するためのイディオムとしては、
    for (int i = 0, length = list.getLength(); i < length; i++) {}
    といったものがあり、IBM developerWorksなどプログラマー向けのメディアでは、この書き方に統一しているものもある。

  • 参照渡し/値渡し関係の質問、switch fall throughなど
    これはそのままなのでパス。
オブジェクト指向編
    ここは全項目そのままなのでパス。
デザインパターン編
  • Singletonパターンとオブジェクト生成コスト、Singletonパターンのマルチスレッド対応
    Singletonの書き方としては、まず下記のようなものが考えられる。

    パターン1. フィールドの初期化時にインスタンス生成

    public class Singleton {
      private static final Singleton self = new Singleton();
      private Singleton() {}
      public static Singleton getInstance() {
        return self;
      }
    }
    パターン2. 遅延初期化を用いて必要になったときにインスタンス生成
    public class Singleton {
      private static Singleton self;
      private Singleton() {}
      public static synchronized Singleton getInstance() {
        if (self == null) {
          self = new Singleton();
        }
        return self;
      }
    }
    通常はパターン1.で良いとして、インスタンス生成コストの大きいオブジェクトを扱う場合にはパターン2.のような遅延初期化の採用が考えられるが、パターン2.の形にしてしまうとマルチスレッド対応が必要な場合にアクセッサーを同期化する必要があり(この例で言うとsynchronizedの箇所)、アクセッサーが頻繁に呼び出されるようなケースではこの同期化のコストを何とかしたいですね、さてどうしましょう、という話。

    ちなみに、Javaに特化した話としては、下記のようにオンデマンド初期化ホルダークラスイディオム(Effective Java 第二版 p.273)を使うと、遅延初期化の恩恵を受けつつ、なおかつ同期化のコストをかけずに済むイディオムもある。

    パターン3. オンデマンド初期化ホルダークラスイディオム

    public class Singleton {
      private static class Holder {
        static final Singleton self = new Singleton();
      }
      private Singleton() {}
      public static Singleton getInstance() {
        return Holder.self;
      }
    }

  • Observerパターンでイベント発火順序が重要な理由
    ・イベント発火順序を正確に把握せずにObserverパターンを使ってしまうと、一つのSubjectに複数のObserverがついており、かつ一方のObserverがもう一方のObserverの値を参照しているような場合にデータの不整合が生じる危険性があるから etc.

  • Mediatorパターンでメソッドが多くなりすぎてきたときの工夫
    ・機能のまとまりごとにインターフェースを用意し、Mediatorはそれらのインターフェースを実装し、Mediator利用者にはインターフェース渡しする
    Role Objectパターンに切り替える
    ・etc.

  • Visitorパターンを使うべき場合と、再帰的ループで対処する場合との使い分け
    単にComposite型のオブジェクトを再帰的に処理したいだけならむやみにVisitorパターンを使うと単にシンプルさが失われるだけになってしまうことがあるので要注意ですよねー、とか、その場合でも様々なところから利用されるようなクラスについては一応Visitorの口も用意してあると親切な設計になる場合もありますよねー、とか、その辺りの話。

  • Compositeパターンが時として「オブジェクト指向的に気持ち悪く」なる理由
    内容と容器を同一視するために、本来一方にしか含まれないインターフェースをもう一方に持たせるケースがあるから。例えば本来は容器にしか含まれない子要素取得系のメソッドが内容の側にも空実装として含まれるケースとか。
■ Java編
  • static/final、List/Set/Mapの違い
    そのままなのでパス。

  • new Boolean(true)、new Integer(5)などが推奨されない理由
    使い回しの利くImmutableなオブジェクトを複数生成して無駄にヒープを汚すのは望ましくないから。代わりにBoolean.valueOf(true)、Integer.valueOf(5)などを使う。

  • 防御的コピーの話
    防御的コピー = フィールドとして持っているオブジェクトを外部に渡すときに複製をつくって渡す。本来防御的コピーが必要なソースを書いてみて、潜在的問題として防御的コピーが行われていないですねー、とか。

  • Javaでのディープコピーの実装方法としてどのようなものが考えられますか
    ・普通にclone()を実装
    ・直列化を利用
    ・etc.

  • バージョンの異なる同一FQNのクラスを利用するための対応方法
    ・VMをわけて何らかの形で通信(RMI etc.)
    ・クラスローダーを自作
    ・etc.
    ちなみにクラスローダーの作り方については以前Java Programming Tipsに書いた記事を参照のこと。


Effective Java 第2版 (The Java Series)
Joshua Bloch
ピアソンエデュケーション
売り上げランキング: 3085
おすすめ度の平均: 5.0
5 中級以上なら必須のマナー
5 Javaの良書


Check このエントリーをはてなブックマークに追加
lalha at 18:09 │プログラミング  │Comments(4)TrackBack(0)

トラックバックURL

この記事へのコメント

1. Posted by nbody   2009年02月25日 20:16
> 代わりにBoolean.valueOf(true)、Integer.valueOf(5)などを使う。
結果は一緒だけどBoolean b = Boolean.TRUEかBoolean b = true;
の方が好み
2. Posted by ぬるぽ   2009年02月27日 10:45
>Integer.valueOf(5)
「頻繁に要求される値をキャッシュする」とあったので
ちょっと気になってJDK1.6の実装を調べたら、
valueOf(int i)は-128〜128の値しかキャッシュされてないぽい。
キャッシュといっても-128〜128の値をもつIntegerオブジェクトをvalueOf(int i)初回呼び出し時に
一気に作成。それ以外は毎回new Interg(int i)を呼び出してる。
なおかつ-128〜128以外の場合はキャッシュに追加されないぽい。毎回オブジェクト
作ってる。しょんぼり(´・ω・`)
3. Posted by 小野和俊   2009年02月27日 14:06
ぬるぽさん

コメントありがとうございます。Integerのインスタンス制御の動作はご指摘の通りで、javadocに記載の「頻繁に要求される値をキャッシュする」という表現だと誤解を生みやすいところはありますね。

「頻繁に」の理解の仕方としては次の二通りがあると思うのですが、

(1) 「一般に」よく使われる値
(2) 「VM起動後に」何度も使われた値

javadocの文書は(1)を指しているのなら正しい、ということになりますが、日本語的に(2)を指しているようにも理解できる、というのは同感です。
(まあでも、そういう場合にはぬるぽさんもそうされたように、ソースを確認しちゃいますけどね :))

解答の方に書いた「使い回しの利くImmutableなオブジェクトを複数生成して無駄にヒープを汚すのは望ましくないから」というのは、一応上の動作を考慮した書き方にしたつもりで、どの値の場合に「使い回しが利く」のかはクラスのインスタンス制御の実装に依存するので、常にキャッシュされることが保証されているわけではないが(逆にそうするとキャッシュオブジェクトの解放のタイミングをどうするかで、明示的に解放する機構を用意するかSoftReferenceを使うか、アプリケーション的な利用頻度の高い値だけをキャッシュする専用クラスをつくるか、とか色々やり方がありますが)、staticファクトリーメソッドが用意されてキャッシュされる可能性があるのに、常にImmutableなクラスを生成するのはヒープに対して乱暴ですよね、というのが言いたかったところです。
4. Posted by てるてるぼうず   2009年04月02日 20:50
ローテク技術者の僕にはよくわからないことばかり。
とても採用にはいたらないと自覚しました。

この記事にコメントする
(スパム対策のため、英数字のみからなるコメントは自動削除されますのでご注意ください。)

名前:
URL:
  情報を記憶: 評価: 顔