Javaでプリミティブvsクラスを使用する場合


54

Javaにはブール(クラス)対ブール(プリミティブ)があることがわかります。同様に、整数(クラス)対int(プリミティブ)があります。クラスに対してプリミティブバージョンを使用する場合のベストプラクティスは何ですか?特定の(パフォーマンス?)理由がない限り、基本的に常にクラスバージョンを使用する必要がありますか?それぞれを使用する最も一般的で受け入れられている方法は何ですか?



私の考えでは、これらはクラスではなく、ボックスです。オブジェクトが必要な場所、つまりコレクション内でプリミティブを使用できるように、それらはそこにあるだけです。2つの整数を追加することはできません(ふりをすることはできますが、実際にはjavaは自動ボックス化|値のボックス化解除を行います)。
ストーンメタル

回答:


47

効果的なJavaの項目5で、Joshua Blochは言います

教訓は明確です。ボックス化されたプリミティブよりもプリミティブを優先し、意図しないオートボクシングに注意してください

クラスの良い使用法の1つは、それらをジェネリック型(リストやマップなどのCollectionクラスを含む)として使用する場合、または暗黙のキャストなしで他の型に変換する場合(IntegerクラスにメソッドdoubleValue()またはがある場合など)byteValue()です。

編集:ジョシュアブロッホの理由:

// Hideously slow program! Can you spot the object creation?
public static void main(String[] args) {
    Long sum = 0L;
    for (long i = 0; i < Integer.MAX_VALUE; i++) {
         sum += i;
    }
    System.out.println(sum);
}

このプログラムは正しい答えを取得しますが、1文字の誤植により、本来よりもはるかに遅いです。変数sumはのLong代わりにとして宣言されますlong。これは、プログラムが約2 ^ 31個の不要なLongインスタンス(long iがに追加されるたびに1つ)を構築することを意味しますLong sum。合計の宣言をからLongに変更するlongと、マシンのランタイムが43秒から6.8秒に短縮されます。


2
Blochの結論を引用するのではなく、Blochの理由を列挙しておけば助かります!
vaughandroid

@Baqueta投稿を編集しました。その理由はパフォーマンスです。
m3th0dman

それは少し明確です、ありがとう。私は今、自分の答えを投稿することに正当性を感じています。:)
vaughandroid

lmaxアーキテクチャは興味深いかもしれません-「もう1桁上に行くにはもう少し賢くなりました。LMAXチームがそこに到達するのに役立つと思ういくつかのことがあります。1つは設計されたJavaコレクションのカスタム実装を書くことですキャッシュフレンドリーでガベージに注意してください。これの例は、特別に記述された配列がMap実装をサポートするハッシュマップキーとしてプリミティブjava longを使用することです

JITが処理すべきもののように見えます
-deFreitas

28

標準的な方法は、ジェネリックを扱う場合を除き、プリミティブを使用することです(オートボックス化とボックス化解除に注意してください!)。

規則に従うことには、いくつかの正当な理由があります。

1.単純な間違いを避けます。

多くの場合、初心者をキャッチするいくつかの微妙な、非直感的なケースがあります。経験豊富なコーダーでさえ、時折スリップしてこれらの間違いを犯します(コードをデバッグしてエラーを見つけたときに宣誓が続くことを願っています!)。

最も一般的な間違いは、のa == b代わりに使用することですa.equals(b)。人々はa == bプリミティブを使用することに慣れているため、オブジェクトラッパーを使用しているときに簡単に実行できます。

Integer a = new Integer(2);
Integer b = new Integer(2);
if (a == b) { // Should be a.equals(b)
    // This never gets executed.
}
Integer c = Integer.valueOf(2);
Integer d = Integer.valueOf(2);
if (c == d) { // Should be a.equals(b), but happens to work with these particular values!
    // This will get executed
}
Integer e = 1000;
Integer f = 1000;
if (e == f) { // Should be a.equals(b)
    // Whether this gets executed depends on which compiler you use!
}

2.読みやすさ:

次の2つの例を考えてみましょう。ほとんどの人は、2番目の方が読みやすいと言います。

Integer a = 2;
Integer b = 2;
if (!a.equals(b)) {
    // ...
}
int c = 2;
int d = 2;
if (c != d) {
    // ...
}

3.パフォーマンス:

事実は、プリミティブを使用するよりも、プリミティブにオブジェクトラッパーを使用する方遅いということです。あらゆる場所で使用するものに、オブジェクトのインスタンス化、メソッド呼び出しなどのコストを追加しています。

Knuthの「...約97%の時間:早すぎる最適化がすべての悪の根源である」という引用は、ここでは実際には当てはまりません。彼は、コード(またはシステム)をより複雑にする最適化について話していました-もしあなたがポイント#2に同意するなら、これはコードをより単純にする最適化です!

4.慣例です:

他のJavaプログラマの99%に対して異なるスタイルの選択を行う場合、2つの欠点があります。

  • 他の人のコードは読みにくいでしょう。99%のexamples / tutorials / etcには、プリミティブが使用されます。あなたがそれを読むときはいつでも、あなたが慣れているスタイルでそれがどのように見えるかについて考える余分な認知オーバーヘッドがあります。
  • 他の人はあなたのコードを読みにくいと感じるでしょう。Stack Overflowで質問するたびに、「プリミティブを使用していない理由」を尋ねる回答/コメントを選別する必要があります。信じられない場合は、生成されたコードに影響を与えないブラケットの配置など、人々の戦いを見てください!

通常、私はいくつかの反論点をリストしますが、正直なところ、ここでコンベンションに参加しない理由は考えられません!


2
オブジェクトとを比較することを提案しないでください==。オブジェクトはと比較する必要がありequals()ます。
Tulainsコルドバ

2
@ user61852私はそれをやることとして提案していませんでしたが、よくある間違いとして!それをもっと明確にすべきですか?
vaughandroid

はい、オブジェクトを比較することは言及していませんequals()...オブジェクトを比較すると==期待される結果が得られるように回避策を与えます。
Tulainsコルドバ

いい視点ね。変更しました。
vaughandroid

equals()2番目のコードスニペットに追加し、投票を変更しました。
Tulainsコルドバ

12

通常、プリミティブを使用します。ただし、Integerとのようなクラスを使用する1つの特性Booleanは、nullこれらの変数に割り当てる可能性です。もちろん、これはnull常にチェックを行う必要があることを意味しますが、NullPointerExceptionを取得する方が、正しく初期化されていない変数intまたはboolean変数を使用するために論理エラーが発生するよりも優れています。

もちろん、Java 8からさらに一歩進むことができ(おそらくそうすべきです)、代わりに、たとえば値を持っているか持っていない変数にInteger使用することができOptional<Integer>ます。

さらに、nullこれらの変数に「不明」または「ワイルドカード」値を割り当てるために使用する可能性が導入されます。これは、Ternary Logicなどのいくつかの状況で便利です。または、特定のオブジェクトがテンプレートに一致するかどうかを確認することもできます。この場合null、オブジェクト内の任意の値を持つことができるテンプレート内の変数に使用できます。


2
(私のダウン投票ではありませんでしたが...)Javaはすでに初期化されていない変数を検出し、その使用に至るすべてのコードパスが値を確実に割り当てるまで変数を読み取らせません。そのためnull、デフォルトとして割り当てる機能から多くを得ることはありません。それどころか、変数を「初期化」しない方がいいでしょう。デフォルト値さえ設定nullすると、コンパイラーがシャットダウンしますが、すべてのコードパスに沿った有用な割り当ての欠如を検出できなくなります。したがって、コンパイラーがキャッチした可能性のあるエラーは、ランタイムにスリップします。
cHao 14

@cHaoしかし、変数を初期化するため賢明なデフォルト値がない場合どうでしょうか?0.0、または-1、またはInteger.MAX_VALUE、またはFalseに設定できますが、最終的にそれがデフォルト値であるか、その変数に割り当てられた実際の値であるかはわかりません。これが重要な場合、null値を設定することでより明確になる可能性があります。
tobias_k

明確ではありません。バグがすでに伝播するまで、不明確なロジックについて警告しないようにコンパイラーに伝える方が簡単です。:P実用的なデフォルトがない場合は、変数を初期化しないください。そこに置くための賢明な値を持っている場合にのみ設定してください。これにより、値が正しく設定されていない場合、コンパイル時に Javaが停止します。
cHao

@cHaoつまり、変数を初期化できず、実行時に変数処理する必要がある場合があります。そのような場合、「null」のような「デフォルト」は、デフォルトまたは「未初期化」として明確に識別でき、有効な値である可能性のあるコンパイル時の初期化よりも優れている場合があります。
tobias_k

そのような場合を念頭に置いていますか?私が考えることができるケースは、インターフェースの境界にあります(例:パラメータまたは戻り値の型として)...しかし、そこであっても、ラップされていればはるかに良いでしょう。(Nakedにnullは、nullパラノイアを含む多くの問題があります。)関数内では、使用時に実際に初期化されていない可能性がある変数は、通常、カバーされていないケースを示します。(明確な割り当て分析は単純化されているため、
誤検知

2

素人の言葉で:

コレクションに物事を追加する必要がある場合は、ラッパーを使用します。

コレクションはプリミティブを保持できません。


0

Javaは、m3th0dmanが指摘したようにオートボクシングを備えています。可能な限り低いレベルで考えてみてください。プリミティブ値のオートボックス化(インまたはアウト)は、アプリケーションのネイティブデータ型で作業している場合、必要のないタスクで費やされるクロックサイクルを意味します。

原則として、可能な限りネイティブデータ型を使用するようにしてください。

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