整数と整数

このトピックでは、intとIntegerの例を使用して、プリミティブ型とそれに対応するオブジェクト型の基本的な違いについて説明します。 これらの違いは非常に単純であり、少し考えるとかなり論理的ですが、経験が示すように、プログラマーは常にそれを考えているわけではありません。



主な違いは、もちろん、整数はヒープ上のスペースを占有する完全に機能するオブジェクトであり、コードでは暗黙的に値に変換されるリンクを使用することです。

int a = 1000 ; // a -

Integer b = 1000 ; // b -




整数型の変数に値を割り当てると、通常、 new Integer(1000)



したかのように、メモリが新しいオブジェクトのヒープに割り当てられます(いわゆるオートボクシング )。 ただし、古いオブジェクトが再利用される場合があります。 これは、次のコード(JDK 1.6)で示されています。

Integer a1 = 50 ;

Integer a2 = 50 ;

Integer b1 = 500 ;

Integer b2 = 500 ;

System. out . println (a1==a2);

System. out . println (b1==b2);




実行の結果は次のようになります。

true

false




ここでは、静的メソッドjava.lang.Integer.valueOf(int)が実際に呼び出されます。これは、 sourcesで確認できるように、-128〜127の値をキャッシュします(最近の実装では、キャッシュの上部境界を変更できます)。



ただし、ほとんどの場合、新しいオブジェクトが作成され、これは重要です。 また、Integerオブジェクトはその値を決して変更しないことに注意してください。 この一見無害なコードを考えてみましょう。

public class Increment

{

public static void main (String[] args)

{

Integer a= 0 ;

while ( true ) a++;

}

}




JProfilerの試用版などで、メモリ使用量をプロファイルしてみましょう。



明らかに、増分ごとに新しいIntegerオブジェクトが作成され、古いオブジェクトは約10万個が蓄積されるとガベージコレクターによってクリーンアップされます。 通常の増分操作に適したシステム負荷。



一般に、Integerがなければ使用できないことは明らかです。 そのような例の1つは、たとえば標準コレクションなどのパラメーター化された型(ジェネリック)です。 ただし、ここではメモリを賢く使用するように注意する必要があります。 実際のプロジェクトで遭遇した問題に基づいて、いくぶん誇張した例を挙げます。 一部の科学的分析では、自然数の長いセットを特定のオブジェクトに関連付けることが必要でした。 次のコードでこれをエミュレートできます。

import java.util.*;



public class MapInteger

{

static Map<Integer, Set<Integer>> subSets = new HashMap<Integer, Set<Integer>>();



public static void put (Integer key, int value)

{

if (!subSets. containsKey (key)) subSets. put (key, new HashSet<Integer>());

subSets. get (key). add (value);

}



public static Collection<Integer> getRandomKeys ()

{

List<Integer> vals = new ArrayList<Integer>();

for ( int i= 0 ; i<( int )(Math. random ()* 500 ); i++)

{

vals. add (( int )(Math. random ()* 1000 ));

}

return vals;

}



public static void main (String[] args)

{

new Scanner(System. in ). nextLine ();

for (Integer i= 0 ; i< 100000 ; i++)

{

for (Integer key: getRandomKeys ())

put (key, i);

}

new Scanner(System. in ). nextLine ();

}

}




最初の10万からの各番号に対して、getRandomKeysを使用してキーのセットを定義し(実際のタスクではもちろん、キーはランダムではありませんでした)、現在の番号を対応するsubSetのセットに追加します。 整数キーのタイプは、図を簡略化するために選択されています。 一般的に、彼は重要ではありません。 操作前のオブジェクトの数は次のとおりです。





しかし、その後:





ガベージコレクターを強制的に起動することはわずかに役立ちました。





40メガバイトのメモリは整数を消費します-悲しいことです。 その理由は、putメソッドのプロトタイプにあります。

public static void put(Integer key, int value)





ここでint型が使用されているため、変数iの値はメソッドに渡されると自動的にintに変換され(ボックス化解除)、次に再び整数に変換されます(ボックス化)が、新しいオブジェクトは既に作成されています。 プロトタイプのint value



Integer value



置き換えて、プロファイラーを再度実行します。 最初は、画像は同じです:





しかし、最後には大きな違いがあります:





ガベージコレクション後:





そのため、1つの整数を整数に置き換えると、使用メモリの約40%を節約できます。 in for(Integer i=0; i<100000; i++)



Integerも理由で使用されることに注意してくださいfor(Integer i=0; i<100000; i++)



ここではintを記述しますが、最初の修正は役に立ちません。 Integerを省略できる場合は常にintを記述する規則が常に機能するとは限らないことがわかります。それぞれの場合について考える必要があります。 整数キャッシュのネイティブ実装も役立つ場合があります。



All Articles