Autoboxing


Javaプログラマならだれでも知っていることですが、int (またはその他のプリミティブ値)をコレクションに置くことはできません。コレクションはオブジェクト参照のみを保持できるため、プリミティブ値は適切なラッパー・クラス(intの場合はInteger)に「詰める(box)」必要があります。オブジェクトをコレクションから取り出すときは、格納したIntegerを取得します。intが必要な場合は、intValueメソッドを使用して、Integerから「取り出す(unbox)」必要があります。これらはすべて煩しい操作であり、コードが複雑化します。Autoboxing/Unboxing機能により、この処理が自動化され、余分な作業やコードの複雑さがなくなります。

次の例では、ジェネリックスおよびfor-eachループを使用したAutoboxing/Unboxing機能を示します。この10行足らずのコードで、コマンド行に現れる語の出現頻度を計算し、アルファベット順に出力します。

import java.util.*;

// Prints a frequency table of the words on the command line
public class Frequency {
   public static void main(String[] args) {
      Map<String, Integer> m = new TreeMap<String, Integer>();
      for (String word : args) {
          Integer freq = m.get(word);
          m.put(word, (freq == null ? 1 : freq + 1));
      }
      System.out.println(m);
   }
}

java Frequency if it is to be it is up to me to do the watusi
{be=1, do=1, if=1, is=2, it=2, me=1, the=1, to=3, up=1, watusi=1}

まず、StringからIntegerへのマップを宣言し、コマンド行に出現する語の出現回数を関連付けます。次に、コマンド行の各語を反復処理します。それぞれの語について、マップ内で語を検索します。改訂した語のエントリをマップに入力します。この操作を行なっている行(緑色で強調表示)には、Autoboxing/Unboxing機能の両方が含まれています。新しい値を計算して語に割り当てるには、まず現在の値(freq)を確認します。この値がnullの場合は、今回が最初の出現になるため、マップに1を入力します。そうでない場合は、これまでの出現回数に1を足し、その値をマップに入力します。もちろんintをマップに入力したり、Integerに1を足すことはできません。実際は、freqに1を足すために自動的にUnboxingされ、int型の式になります。条件式内の代替式の両方がint型であるため、この条件式自体もintになります。このint値をマップに入力するために、IntegerにAutoboxingします。

この操作全体の結果として、いくつかの注意点を除いて、intIntegerの区別をほとんど無視することができるのです。Integer式では、null値を使用できます。nullを自動的にUnboxingしようとすると、NullPointerExceptionがスローされます。==演算子は参照の同一性比較をInteger式で実行し、値の等価性比較をint式で実行します。最後に、Autoboxing/Unboxingは自動的に行われますが、パフォーマンス上の負荷はかかります。

次に、Autoboxing/Unboxing機能について、別のサンプル・プログラムを示します。int配列を取るstaticファクトリであり、配列に基づくIntegerListを返します。この10行足らずのコードで、このメソッドは、int配列に基づいてListインタフェースの豊富な機能を提供します。リストに対するすべての変更は、配列に書き込まれます。逆も同様です。Autoboxing/Unboxing機能の行は、緑色で強調表示されています。

// List adapter for primitive int array
public static List<Integer> asList(final int[] a) {
    return new AbstractList<Integer>() {
        public Integer get(int i) { return a[i]; }
        // Throws NullPointerException if val == null
        public Integer set(int i, Integer val) {
            Integer oldVal = a[i];
            a[i] = val;
            return oldVal;
        }
        public int size() { return a.length; }
    };
}

結果のリストのパフォーマンスは良くありません。これは、Autoboxing/Unboxingがgetまたはset操作ごとに行われるためです。このコードは、たまに使用するのであれば十分高速ですが、パフォーマンスが重要となる内部ループで使用することは避けてください。

それではAutoboxing/Unboxing機能はいつ使用すべきでしょうか。これらは、参照型とプリミティブとの間に「インピーダンス・ミスマッチ」がある場合にのみ使用してください。たとえば数値をコレクションに入れる必要がある場合です。Autoboxing/Unboxing機能を科学計算やパフォーマンスが重要な数値コードに使用することは、適切ではありませんIntegerintの代わりになりません。Autoboxing/Unboxing機能により、プリミティブ型と参照型の区別があいまいになりますが、取り除かれるわけではありません。


Copyright © 1993, 2020, Oracle and/or its affiliates. All rights reserved.