なぜ人々はまだJavaでプリミティブ型を使用するのですか?


163

Java 5以降、プリミティブ型のボクシング/アンボクシングがint行われるようjava.lang.Integerになりました。

私は(という最近の新しいJavaプロジェクトの多くを参照してください間違いなく使用している少なくともバージョン5のJREを必要とし、そうでない場合は6)intのではなくjava.lang.Integer、それは変換するためのいくつかのヘルパーメソッドを持っているとして、それは、後者を使用することがはるかに便利だけれども、long値等へ

なぜいくつか がJavaでまだプリミティブ型を使用ですか?目に見えるメリットはありますか?


49
メモリの消費とパフォーマンスについて考えたことはありますか?
Tedil、2011年

76
オートボクシングタグを追加したところ、3人が実際にフォローしていることがわかりました。本当に?人々はAUTOBOXINGタグをフォローしていますか?
corsiKa

4
@glowcoderそれらは実際の人々ではなく、SOで返信するために人間の形をとる抽象的な概念です。:)
biziclop

9
@TK Kocheranほとんどの場合new IntegeR(5) == new Integer(5)、ルールにより、falseと評価されます。
biziclop 2011年

10
プリミティブ型のコレクションの解決策については、GNU TroveまたはMahoutコレクションまたはHPPCまたは...を参照してください。スピードを重視する人は、プリミティブ型を使用することに時間を費やしています。
bmargulies

回答:


395

ジョシュア・ブロックの効果的なJava項目5:「不要なオブジェクトの作成を避ける」で、彼は次のコード例を投稿します。

public static void main(String[] args) {
    Long sum = 0L; // uses Long, not long
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i;
    }
    System.out.println(sum);
}

実行には43秒かかります。Longをプリミティブに取り込むと、6.8秒に短縮されます...それが何らかの理由でプリミティブを使用する理由である場合。

ネイティブバリューの平等の欠如も懸念事項です(.equals()と比較するとかなり冗長です)。==

ビジクロップの場合:

class Biziclop {

    public static void main(String[] args) {
        System.out.println(new Integer(5) == new Integer(5));
        System.out.println(new Integer(500) == new Integer(500));

        System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
        System.out.println(Integer.valueOf(500) == Integer.valueOf(500));
    }
}

結果:

false
false
true
false

編集 なぜ(3)戻るtrueと(4)戻るかfalseですか?

2つの異なるオブジェクトだからです。ゼロに最も近い256の整数[-128; 127]はJVMによってキャッシュされるので、それらは同じオブジェクトを返します。ただし、その範囲を超えると、キャッシュされないため、新しいオブジェクトが作成されます。物事をより複雑にするために、JLS は少なくとも 256のフライウェイトがキャッシュされること要求します。JVMの実装者は、必要に応じてさらに追加することがあります。これは、最も近い1024がキャッシュされ、すべてがtrueを返すシステムで実行できることを意味します... #awkward


54
同様にi宣言されLongているかどうか想像してみてください!
ColinD

14
@TREE-仕様では、実際に VMが特定の範囲内のフライウェイトを作成する必要があります。しかし、残念なことに、その範囲を拡張することができます。つまり、プログラムがVMごとに異なる動作をする可能性があります。クロスプラットフォームとしてはこれで十分です...
Daniel Earwicker、2011年

12
Javaは、ますます悪い設計の選択を伴って、ドレインを使い果たしました。オートボクシングは完全な障害であり、堅牢でも予測可能でも移植性もありません。私は本当に彼らが何を考えていたのだろうと思います...恐ろしいプリミティブオブジェクトの二重性を修正する代わりに、彼らは何よりもまずそれを悪化させました。
Pop Catalin、2011年

34
@Catalinオートボクシングは完全な失敗だと私はあなたに同意しません。いくつかの欠陥があり、使用された可能性のある他の設計と何も変わらない(何も含まない)。それらは、何が期待でき、何が期待できないかを非常に明確にし、開発者が契約を知って従うと期待する他の設計と同様にそれらのデザインの。
corsiKa 2011年

9
@NaftuliTzviKayそれは「失敗」ではありません。これらは、演算子が式の参照ID比較と式の値の等価比較を実行することを非常に明確にします。まさにこの理由で存在しています。あなたは必要があります決して使用しない任意の非プリミティブ型の値を比較します。これはJavaの101です==IntegerintInteger.equals()==
NullUserException

86

自動ボックス化解除により、NPEを見つけるのが困難になる可能性があります

Integer in = null;
...
...
int i = in; // NPE at runtime

ほとんどの場合、null割り当てinは上記よりもはるかに明白ではありません。


43

ボックス型はパフォーマンスが低く、より多くのメモリを必要とします。


40

プリミティブタイプ:

int x = 1000;
int y = 1000;

次に評価します。

x == y

ですtrue。ほとんど驚くことではありません。ボックス化された型を試してください:

Integer x = 1000;
Integer y = 1000;

次に評価します。

x == y

ですfalse。恐らく。ランタイムに依存します。その理由は十分ですか?


36

パフォーマンスとメモリの問題のほかに、私は別の問題を思い付くしたいと思います:Listインターフェイスはせずに壊れてしまいますint
問題はオーバーロードされたremove()メソッド(remove(int)vs. remove(Object))です。 remove(Integer)常に後者の呼び出しに解決されるため、インデックスで要素を削除できませんでした。

一方、を追加および削除しようとすると、落とし穴がありますint

final int i = 42;
final List<Integer> list = new ArrayList<Integer>();
list.add(i); // add(Object)
list.remove(i); // remove(int) - Ouch!

7
はい、壊れます。ただし、remove(int)は設計上の欠陥であるIMOです。混同する可能性が少しでもある場合は、メソッド名をオーバーロードしないでください。
MrBackend 2015

4
@MrBackend十分に公正です。興味深いことに、最初からVector持っていremoveElementAt(int)ました。remove(int)Java 1.2のコレクションフレームワークで導入されました。
xehpuk 2015

6
@MrBackend:ときListAPIが設計された、存在していたジェネリック医薬品やオートボクシングでもないので、何の混同の可能性がなかったremove(int)remove(Object)...
ホルガー

@Franklin Yu:確かに、互換性の制約のない新しい言語/バージョンを設計するときは、残念なオーバーロードの変更に止まらないでしょう。プリミティブとボックス化された値の区別を完全に取り除くだけなので、どちらを使用するかという質問が表示されることはありません。
Holger

27

あなたは本当に想像できますか

  for (int i=0; i<10000; i++) {
      do something
  }

代わりにjava.lang.Integerでループしますか?java.lang.Integerは不変であるため、単一のJVM命令でスタックのintをインクリメントするのではなく、ループの各インクリメントにより、ヒープ上に新しいjavaオブジェクトが作成されます。パフォーマンスは悪魔的なものになります。

intよりもjava.lang.Integerを使用する方がはるかに便利なモードであることに、私は本当に反対します。それどころか。オートボクシングとは、Integerを使用せざるを得ない場合にintを使用できることを意味し、Javaコンパイラーがコードを挿入して新しいIntegerオブジェクトを作成します。オートボクシングとは、コンパイラーが関連するオブジェクト構造を挿入して、整数が期待される場所でintを使用できるようにすることです。それは、そもそもintの必要性を取り除くことも減らすこともしません。オートボクシングを使用すると、両方の長所を利用できます。ヒープベースのJavaオブジェクトが必要な場合は、Integerが自動的に作成され、算術計算とローカル計算を実行しているだけの場合は、intの速度と効率が得られます。


19

プリミティブ型ははるかに高速です:

int i;
i++;

整数(すべての数値と文字列)は不変の型です。一度作成すると変更できません。iIntegerの場合i++、新しいIntegerオブジェクトを作成するよりも、メモリとプロセッサの点ではるかに高価です。


i++別の変数を変更する場合、1つの変数を変更したくないので、これを行うには、Integerを完全に不変にする必要があります(少なくとも、i++とにかく新しいIntegerオブジェクトを作成する必要があります)。(そして、プリミティブ値も不変です-オブジェクトではないので、これに注意しないでください。)
PaŭloEbermann

4
@Paŭlo:プリミティブ値が不変であると言っても意味がありません。プリミティブ変数を新しい値に再割り当てしても、新しいものは作成されません。メモリの割り当ては含まれません。ピーターの要点は、プリミティブのi ++はメモリ割り当てを行わないが、オブジェクトの場合は必ず行うということです。
エディ

@Eddie:(メモリ割り当ては必ずしも必要ではありません。キャッシュされた値を返すこともできます。小さな値の場合はそうだと思います。)私のポイントは、ここでの整数の不変性は決定的なポイントではないということです。不変性に関係なく、別のオブジェクトを持つこと。
–PaŭloEbermann、2011

@Paŭlo:私の唯一のポイントは、整数はプリミティブよりも桁違いに遅いということでした。これは、ボックス化された型は不変であり、値を変更するたびに新しいオブジェクトが作成されるためです。私は彼らに何か問題がある、またはそれらが不変であるという事実を主張しませんでした。それらが遅いことと、コーダーがそれを知っている必要があるということだけです。プリミティブ型なしでのGroovyの運賃を見てみましょうjroller.com/rants/entry/why_is_groovy_so_slow
Peter

1
不変性と++は、ここの赤いニシンです。Javaが拡張されて、非常に単純な方法で演算子のオーバーロードをサポートするように拡張されたとします。たとえば、クラス(Integerメソッドがあるなど)の場合は、の代わりにplus記述できます。また、コンパイラがに拡張できるほどスマートであると想定します。変更可能ではなく、効果的に「変数iをインクリメント」i + 1i.plus(1)i++i = i + 1i++Integer
Daniel Earwicker

16

何よりもまず、習慣。8年間Javaでコーディングした場合、かなりの量の慣性が蓄積されます。やむを得ない理由がないのに、なぜ変更するのですか?ボックス化されたプリミティブを使用すると、特別な利点が得られるわけではありません。

もう1つの理由はnull、これが有効なオプションではないと断言するためです。2つの数値の合計またはループ変数を次のように宣言することは無意味で誤解を招くでしょう。Integer

それにはパフォーマンスの側面もありますが、多くの場合、パフォーマンスの違いはそれほど重要ではありませんが(実際のところ、それはかなり悪いことです)、すでに手早く簡単に記述できるコードを書くのは好きではありません。慣れている。


15
同意しません。パフォーマンスの側面が重要になる場合があります。これのほんの少しはおそらく慣性または習慣の力です。
Eddie

7
@エディそれは可能ですが、それは非常にまれです。信じてください、ほとんどの人にとって、パフォーマンスの議論は言い訳にすぎません。
biziclop 2011年

3
私もパフォーマンスの議論を保護したいと思います。Dalvikを使用するAndroidでは、作成する各オブジェクトにより、呼び出されるGCの「リスク」が高まり、一時停止するオブジェクトが増えるほど、時間が長くなります。したがって、ループでintの代わりにIntegerを作成すると、おそらくいくつかのフレームがドロップすることになります。
IgorČordaš2014

1
@PSIXOそれは公平な点です。純粋にサーバー側のJavaを念頭に置いて記述しました。モバイルデバイスはまったく別の動物です。しかし、私の指摘は、パフォーマンスに関係なくひどいコードを書く開発者でさえ、これを理由として引用するということです。彼らからは、これは言い訳として非常に聞こえます。
ビジクロップ2014

12

ちなみに、Smalltalkにはオブジェクトしかなく(プリミティブはありません)、ヒープ領域を割り当てずに特別なビットパターンを使用するために、スモール整数(32ビットすべてではなく、27ビットのみなど)を最適化しています。また、他の一般的なオブジェクト(true、false、null)には、ここに特別なビットパターンがありました。

したがって、少なくとも64ビットのJVM(64ビットのポインター名前空間を使用)では、整数、文字、バイト、ショート、ブール、フロート(およびロング)のオブジェクトをまったく作成できないようにする必要があります(これらの作成は別として)明示的にnew ...())、通常の演算子で非常に効率的に操作できる特別なビットパターンのみ。


これは言語仕様に支配されないので、「いくつかの実装」と言った方がいいと思います。(そして
残念なことに

ŭlo、JITは既にポインターをメタに保持しています。incl、ポインタはGC情報またはクラスを保持できます(クラスを最適化することは、整数を最適化するよりもはるかに優れています。ポインターを変更するには、各ポインターをロードする前に、shift / cmp / jnzコード(またはそのようなもの)が必要です。ブランチはおそらくハードウェアによって十分に予測されず(値タイプと通常のオブジェクトの両方になる可能性があるため)、パフォーマンスに影響を与えます。
bestss

3
私は数年間Smalltalkをしました。intの各操作では、マスクを解除して再適用する必要があったため、最適化は依然としてかなり高価でした。現在、プリミティブ番号を操作するとき、JavaはCと同等です。unmask + maskを使用すると、30%以上遅くなる可能性があります。
R.Moeller

9

私が最も重要な理由であると私が思うことについて誰も言及しなかったとは信じられません。 "int"はそうであり、 "Integer"よりもはるかに簡単に入力できます。簡潔な構文の重要性を過小評価していると思います。数値を使用しているほとんどの場合はループインデックス内にあり、それらのコストをインクリメントおよび比較することは、(intまたはIntegerを使用しているかどうかにかかわらず)非自明なループでは何も行わないので、パフォーマンスは実際にそれらを回避する理由ではありません。

もう1つの理由は、NPEを取得できることですが、ボックス化された型を使用すると非常に簡単に回避できます(常に非ヌル値に初期化する限り、回避することが保証されます)。

もう1つの理由は、(new Long(1000))==(new Long(1000))がfalseであることですが、これは、「。equals」がボックス化された型の構文サポートを持たない(演算子<、>とは異なり) 、=など)なので、「単純な構文」の理由に戻ります。

Steve Yeggeの非プリミティブループの例は、私のポイントを非常によく表しています。 http

これについて考えてください。RunnableやCallableなどのインターフェースを使用してそれらをシミュレートする必要があるjavaと比較して、適切な構文を持つ関数型(関数型言語、python、ruby、さらにはCなど)で関数型を使用する頻度名前のないクラス。


8

プリミティブを削除しない2つの理由:

  • 後方互換性。

それが排除された場合、古いプログラムは実行されません。

  • JVMの書き換え。

この新しいものをサポートするには、JVM全体を書き直す必要があります。

  • より大きなメモリフットプリント。

より多くのメモリを使用する値と参照を保存する必要があります。巨大なバイト配列がある場合、を使用するbyte方がを使用するよりも大幅に小さくなりByteます。

  • ヌルポインターの問題。

宣言してint iから処理を行うとi問題は発生しませんが、宣言Integer iしてから同じ処理を行うとNPEが発生します。

  • 平等の問題。

このコードを考えてみましょう:

Integer i1 = 5;
Integer i2 = 5;

i1 == i2; // Currently would be false.

間違いでしょう。オペレーターは過負荷になる必要があり、その結果、内容が大幅に書き換えられます。

  • スロー

オブジェクトラッパーは、プリミティブのオブジェクトラッパーよりも大幅に低速です。


i1 == i2; i1> = 128の場合にのみ偽になります。したがって、現在の例は間違っています
Geniy

7

オブジェクトはプリミティブ型よりもはるかに重いため、プリミティブ型はラッパークラスのインスタンスよりもはるかに効率的です。

プリミティブ型は非常に単純です。たとえば、intは32ビットで、メモリ内でちょうど32ビットを占有し、直接操作できます。Integerオブジェクトは完全なオブジェクトであり、(他のオブジェクトと同様に)ヒープに格納する必要があり、オブジェクトへの参照(ポインタ)を介してのみアクセスできます。また、32ビット(4バイト)以上のメモリを使用する可能性もあります。

そうは言っても、Javaがプリミティブ型と非プリミティブ型を区別しているという事実も、Javaプログラミング言語の時代の兆候です。新しいプログラミング言語にはこの違いはありません。そのような言語のコンパイラーは、単純な値やより複雑なオブジェクトを使用している場合に、それ自体で理解できるほどスマートです。

たとえば、Scalaにはプリミティブ型はありません。整数用のクラスIntがあり、Intは実際のオブジェクトです(メソッドなどを実行できます)。コンパイラーがコードをコンパイルするときに、舞台裏でプリミティブintを使用するため、Javaでプリミティブintを使用するのと同じくらい効率的にIntを使用できます。


1
JREは、Javaでラップされたプリミティブでもこれを実行するのに十分「スマート」であると想定していました。不合格。
Naftuli Kay、2011

7

他の人が言ったことに加えて、プリミティブローカル変数はヒープからではなくスタックに割り当てられます。ただし、オブジェクトはヒープから割り当てられるため、ガベージコレクションを行う必要があります。


3
すみません、これは間違っています。スマートJVMは、オブジェクト割り当てのエスケープ分析を実行でき、エスケープできない場合は、スタックに割り当てます。
rlibby

2
はい、これは最近のJVMの機能になり始めています。5年後、あなたが言うことは、その時使用されているほとんどのJVMに当てはまります。今日はそうではありません。私はこれについてほとんどコメントしましたが、コメントしないことにしました。たぶん私は何か言ったはずだった。
Eddie

6

プリミティブタイプには多くの利点があります。

  • 書くのが簡単なコード
  • 変数のオブジェクトをインスタンス化しないため、パフォーマンスが向上します
  • それらはオブジェクトへの参照を表していないため、nullをチェックする必要はありません
  • ボクシング機能を利用する必要がない限り、プリミティブ型を使用します。

5

内部でどのような最適化が行われているかを知ることは困難です。

ローカルで使用する場合、コンパイラーがnull値の可能性を除外して最適化を行うのに十分な情報を持っている場合、パフォーマンスは同じか類似していると期待します

ただし、プリミティブの配列は、ボックス化されたプリミティブのコレクションとは明らかに非常に異なります。これは、コレクションの奥深くで可能な最適化がほとんどないことを考えると理にかなっています。

さらに、と比較すると、論理オーバーヘッドがIntegerはるかに高くなります。ここで、例外をスローするかどうかについて心配する必要があります。intint a = b + c;

私はプリミティブをできる限り使用し、ファクトリーメソッドとオートボクシングに依存して、必要なときに意味論的に強力なボックス型を提供します。


5
int loops = 100000000;

long start = System.currentTimeMillis();
for (Long l = new Long(0); l<loops;l++) {
    //System.out.println("Long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around Long: "+ (System.currentTimeMillis()- start));

start = System.currentTimeMillis();
for (long l = 0; l<loops;l++) {
    //System.out.println("long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around long: "+ (System.currentTimeMillis()- start));

Longで「100000000」回ループするのにかかるミリ秒:468

長い間「100000000」回ループするのにかかるミリ秒:31

余談ですが、このようなものが表示されても、Javaへの道のりはわかります。

Integer loop1 = new Integer(0);
for (loop1.lessThan(1000)) {
   ...
}

forループがloop1を0から1000に自動的にインクリメントするか、

Integer loop1 = new Integer(1000);
for (loop1.greaterThan(0)) {
   ...
}

forループがloop1 1000を自動的に0に減らします。


2
  1. 数学演算を行うにはプリミティブが必要です
  2. プリミティブは、上記で答えたようにメモリが少なくなり、パフォーマンスが向上します

クラス/オブジェクト型が必要な理由を尋ねる必要があります

オブジェクト型を使用する理由は、コレクションを扱うときに私たちの生活を楽にするためです。プリミティブをリスト/マップに直接追加することはできません。ラッパークラスを作成する必要があります。ここでは、Readymade Integerの一種のクラスが役立ちます。また、Integer.pareseInt(str)などの多くのユーティリティメソッドがあります。


2

以前の回答に同意します。プリミティブラッパーオブジェクトを使用すると、コストが高くなる可能性があります。ただし、アプリケーションでパフォーマンスが重要でない場合は、オブジェクトの使用時にオーバーフローを回避できます。例えば:

long bigNumber = Integer.MAX_VALUE + 2;

の値bigNumberは-2147483647であり、2147483649であることが予想されます。これは、次のようにして修正されるコードのバグです。

long bigNumber = Integer.MAX_VALUE + 2l; // note that '2' is a long now (it is '2L').

そしてbigNumber時々見逃すことが容易であり、未知の行動や脆弱性につながる可能性のバグのこれらの種類2147483649になります(CWE-190)。

ラッパーオブジェクトを使用する場合、同等のコードはコンパイルされません。

Long bigNumber = Integer.MAX_VALUE + 2; // Not compiling

したがって、プリミティブラッパーオブジェクトを使用すると、この種の問題を簡単に回避できます。

あなたの質問はすでに回答されているので、以前に言及されていない情報をもう少し追加するために返信します。


1

JAVAはすべての数学演算をプリミティブ型で実行するためです。この例を考えてみましょう:

public static int sumEven(List<Integer> li) {
    int sum = 0;
    for (Integer i: li)
        if (i % 2 == 0)
            sum += i;
        return sum;
}

ここでは、リマインダーと単項プラスの操作はInteger(Reference)型には適用できません。コンパイラーはボックス化解除を実行して操作を実行します。

したがって、Javaプログラムでオートボクシングとアンボクシングの操作がいくつ行われるかを確認してください。なので、この操作には時間がかかります。

一般に、参照型の引数とプリミティブ型の結果を保持することをお勧めします。


1

プリミティブ型はありはるかに高速かつ非常に必要と少ないメモリを。したがって、これらを使用することをお勧めします。

一方、現在のJava言語仕様では、JavaコレクションまたはリフレクションAPIのパラメーター化された型(ジェネリック)でプリミティブ型を使用できません。

アプリケーションで多数の要素を含むコレクションが必要な場合は、できるだけ「経済的な」タイプの配列の使用を検討する必要があります。

*詳細については、ソースを参照してくださいhttps : //www.baeldung.com/java-primitives-vs-objects


0

簡単に言うと、プリミティブ型はボックス型よりも高速で、必要なメモリも少なくなります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.