Java APIがshortまたはbyteではなくintを使用するのはなぜですか?


回答:


166

その理由のいくつかはすでに指摘されています。たとえば、「...(ほぼ)すべての操作がバイト、ショートでこれらのプリミティブをintに昇格する」という事実です。しかし、明らかに次の質問は次のようになります。なぜこれらのタイプはに昇格されていますかint

したがって、さらに1レベル進んでいきます。答えは、Java仮想マシン命令セットに関連しているだけかもしれません。まとめたように、Java仮想マシン仕様のテーブルすべての積分演算は、加算分割などのような、唯一のタイプのために利用可能であるintとタイプlong、およびいない小さいタイプの。

(余談:小さい型(byteshort)は基本的に配列のみを対象としています配列のようなものnew byte[1000]は1000バイト、配列のようなものnew int[1000]は4000バイトかかります)

もちろん、次のintlongように言うこともできます。「...次の明らかな質問は次のとおりです。なぜこれらの指示は(およびに対してのみ提供されるのですか?」

1つの理由は、上記のJVM仕様に記載されています。

型指定された各命令がJava仮想マシンのすべての実行時データ型をサポートしている場合、1バイトで表すことができるよりも多くの命令があります。

さらに、Java仮想マシンは実際のプロセッサーの抽象概念と見なすことができます。また、小型のタイプに専用の算術論理演算ユニットを導入しても、努力する価値はありません。追加のトランジスタが必要ですが、それでも、1つのクロックサイクルで1つの加算しか実行できません。JVMが設計されたときの主要なアーキテクチャは32ビットであり、32ビットに最適intです。(64ビットlong値を含む操作は、特殊なケースとして実装されます)。

(注:最後の段落は、可能なベクトル化などを考慮して、少し単純化しすぎていますが、プロセッサーの設計トピックに深く入り込むことなく、基本的な考え方を提供する必要があります)


編集:質問の例に焦点を当てた短い補遺ですが、より一般的な意味で:小さい型を使用してフィールドを格納することが有益ではないかどうかを尋ねることもできます。たとえば、メモリをCalendar.DAY_OF_WEEKとして保存することでメモリを節約できると考えるかもしれませんbyte。しかしここでは、Javaクラスファイル形式が使用されます。クラスファイル内のすべてのフィールドは、サイズが1 int(32ビット)の少なくとも1つの「スロット」を占有します。(「ワイド」フィールド、doubleおよびlongは2つのスロットを占有します)。したがって、フィールドを明示的に宣言しshortたりbyte、メモリを節約したりすることもありません。


オペランドがintに昇格される理由の論理は、CおよびC ++で
思い

@ Marco13「フィールドをshortまたはbyteとして明示的に宣言しても、メモリは節約されません。」本当?私はそれが正しいとは思いません。
ACV 2017

@ACV厳密に言うと、実装はよりコンパクトなフォームを格納することを選択できますが、「仮想」で(つまり、仮想マシンによって)公開されるフォーマットは、値を少なくとものサイズを持つものとして扱いますint。別の実装への参照がある場合は、答えを更新し、それに応じてリンクを挿入します。
Marco13 2017

40

(ほぼ)上のすべての操作はbyteshortそれらをintに昇格させます。たとえば、次のように書くことはできません。

short x = 1;
short y = 2;

short z = x + y; //error

を使用するとint、算術演算がより簡単で簡単になり、キャストする必要がありません。

スペースに関しては、ほとんど違いがありません。byteそしてshort、物事を複雑になり、私たちは、変数の一定量について話しているので、このマイクロ最適化の価値は、それを考えていません。

byte組み込みデバイス用にプログラムしたり、ファイル/ネットワークを処理する場合に、関連性があり便利です。また、これらのプリミティブは制限されていますが、将来的に計算がその制限を超える可能性がある場合はどうなりますか?Calendar数が増える可能性があるクラスの拡張について考えてみてください。

また、64ビットプロセッサでは、ローカルはレジスタに保存され、リソースを使用しないためintshortや他のプリミティブを使用してもまったく違いがありません。さらに、多くのJava実装は変数*(およびオブジェクト)を整列させます。


* あたかもローカル変数、クラス変数、またはインスタンス変数であるかのように同じスペースbyteshort占有します。どうして?(ほとんどの)コンピューターシステムでは、変数のアドレスが揃えられているため、たとえば1バイトを使用すると、実際には2バイトになります。1つは変数自体用で、もう1つはパディング用です。int

一方、配列では、byte1バイト、short2バイト、int4バイトを使用します。これは、配列では配列の開始と終了だけを揃える必要があるためです。これにより、たとえばを使用したい場合に違いが生じSystem.arraycopy()、実際にパフォーマンスの違いがわかります。


1
面白い事実:両方の値にfinal修飾子を使用すると、機能します。:)
アレクサンダー2017年

7

整数を使用すると、shortと比較して算術演算が容易になるためです。定数が実際にshort値でモデル化されていると仮定します。次に、この方法でAPIを使用する必要があります。

short month = Calendar.JUNE;
month = month + (short) 1; // is july

明示的なキャストに注意してください。短い値はint、算術演算で使用されるときに暗黙的に値に昇格されます。(オペランドスタックでは、shortはintとして表現されることもあります。)これは、使用するのが非常に面倒intです。そのため、定数には値がよく使用されます。

それと比較すると、一定数のそのような定数しか存在しないため、ストレージ効率の向上は最小限です。40の定数について話している。ストレージをからintに変更するshortと安全です40 * 16 bit = 80 byte。詳細については、この回答を参照してください。


5

整数定数が収まる最小の型で格納されるという哲学を使用した場合、Javaは深刻な問題を抱えることになります。プログラマーが整数定数を使用してコードを書くときはいつでも、コードに注意して型をチェックする必要があります。定数は重要であり、そうであれば、ドキュメントで型を検索し、必要な型変換を実行します。

深刻な問題の概要を説明したところで、その哲学でどのような利点を達成できますか?その変更の実行時に観察可能な唯一の効果が、リフレクションを介して定数を調べたときに得られるタイプである場合、私は驚くことではありません。(そしてもちろん、遅延/無意識のプログラマによって導入されたエラーは、定数の型を正しく説明していません)

長所と短所を比較検討することは非常に簡単です。それは悪い哲学です。


4

仮想マシンの設計の複雑さは、仮想マシンが実行できる操作の種類の関数です。さらに、「乗算」のような命令の4つの実装(32ビット整数、64ビット整数、32ビット浮動小数点、および64ビット浮動小数点に1つずつ)を実装する方が、さらに、上記のように、より小さな数値タイプのバージョンも同様です。より興味深い設計上の疑問は、型の数を減らすのではなく、4種類にする必要があることです(64ビット整数ですべての整数計算を実行するか、64ビット浮動小数点値ですべての浮動小数点計算を実行する)。32ビット整数を使用する理由は、32ビット型が16ビットまたは8ビット型と同じくらい迅速に処理できる多くのプラットフォームでJavaが実行されることが期待されていたが、64ビット型での操作は著しくなるためです。もっとゆっくり。32ビットタイプのみです。

32ビット値で浮動小数点計算を実行することに関しては、利点は少し明確ではありません。のような計算が行われるいくつかのプラットフォームがありますfloat a=b+c+d;すべてのオペランドをより高精度の型に変換し、それらを追加してから、結果を32ビット浮動小数点数に変換して格納することにより、最も迅速に実行できます。32ビット浮動小数点値を使用してすべての計算を実行する方が効率的なプラットフォームは他にもあります。Javaの作成者は、すべてのプラットフォームで同じように処理する必要があること、およびPCの速度が大幅に低下したとしても、32ビット浮動小数点の計算がより長いハードウェアプラットフォームを優先すべきであると判断しました。典型的なPCや、浮動小数点ユニットを持たない多くのマシンでの浮動小数点演算の精度。btw、b、c、dの値に応じて、前述のような式を計算するときに高精度の中間計算を使用することに注意してください。float a=b+c+d;時々、すべての中間オペランドがfloat正確に計算されたものよりもはるかに正確な結果が得られますが、正確さが少し低い値が得られることもあります。いずれにせよ、Sunはすべてを同じ方法で行うべきだと判断し、最小精度のfloat値を使用することを選択しました。

小さいデータ型の主な利点は、それらが多数配列に格納されている場合に明らかになります。64ビットよりも小さい型の個々の変数を持つことに利点がなかったとしても、より小さな値をよりコンパクトに格納できる配列を用意することは価値があります。ローカル変数をa byteではなくaにすると、long7バイトが節約されます。1,000,000個の数値の配列があると、各数値bytelong波は7,000,000バイト。各配列タイプは、いくつかの操作(特に、1つのアイテムの読み取り、1つのアイテムの格納、配列内のアイテムの範囲のコピー、またはある配列から別のアイテムへのアイテムの範囲のコピー)をサポートするだけでよいため、さらに複雑になる配列型は、直接使用できる離散数値の型が増えるほど複雑ではありません。


2

実際には、小さな利点があります。あなたが持っている場合

class MyTimeAndDayOfWeek {
    byte dayOfWeek;
    byte hour;
    byte minute;
    byte second;
}

典型的なJVMでは、単一のを含むクラスと同じだけのスペースが必要intです。メモリ消費量は次の8または16バイトの倍数に丸められるため(IIRC、これは構成可能です)、実際に節約されるケースはかなりまれです。

このクラスは、対応するCalendarメソッドがを返した場合、少し使いやすくなりますbyte。しかし、そのようなCalendarメソッドはなく、他のフィールドのためにをget(int)返す必要があるだけintです。小さい型に対する各操作はに昇格するintため、多くのキャストが必要です。

ほとんどの場合、あなたはあきらめて、intまたはに切り替えるか、

void setDayOfWeek(int dayOfWeek) {
    this.dayOfWeek = checkedCastToByte(dayOfWeek);
}

DAY_OF_WEEKとにかく、タイプは関係ありません。

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