Javaには、byte
、short
、int
、long
のプリミティブ型があり、float
とdouble
。プリミティブ値に使用するバイト数を人に設定する必要があるのはなぜですか?渡された数値の大きさによっては、サイズを動的に決定できなかっただけです?
私が考えることができる2つの理由があります:
単一のint
とfloat
型を使用するだけで多くのことが得られたと思いますが、特定の理由があった場合Javaこのルートに行きますか?
言語設計の多くの側面と同様に、エレガンスとパフォーマンスのトレードオフになります(以前の言語からの歴史的な影響は言うまでもありません)。
単一のタイプの自然数nat
を持つプログラミング言語を作成することは確かに可能です(そして非常に単純です)。学術研究に使用されているほとんどすべてのプログラミング言語(PCF、システムFなど)はこの単一の数値型を持っています。これは、ご想像のとおり、よりエレガントなソリューションです。しかし、実際の言語設計は優雅さだけではありません。パフォーマンスも考慮する必要があります(パフォーマンスがどの程度考慮されるかは、言語の使用目的によって異なります)。パフォーマンスには、時間とスペースの両方の制約が含まれます。
プログラマが事前にバイト数を選択できるようにすると、メモリに制約のあるプログラムのスペースを節約できます。すべての数値が256未満になる場合は、byte
sの8倍のlong
sを使用するか、保存されたストレージをより複雑なオブジェクトに使用できます。標準のJavaアプリケーション開発者は、これらの制約について心配する必要はありませんが、実際に発生します。
スペースを無視しても、CPUの制約を受けます。CPUには、固定バイト数(64ビットアーキテクチャでは8バイト)で動作する命令しかありません。つまり、単一の8バイトのlong
型を指定しても、算術演算をsingleに直接マッピングできるため、無制限の自然数型よりも言語の実装が大幅に簡素化されます。 =基になるCPU命令。プログラマが任意の数を使用できるようにする場合は、単一の算術演算をsequenceの複雑な機械語命令にマッピングする必要があり、プログラムの速度が低下します。これがあなたが育てたポイント(1)です。
これまでの議論は整数にのみ関係してきました。浮動小数点型は、非常に微妙なセマンティクスとエッジケースを持つ複雑な獣です。したがって、int
、long
、short
、およびbyte
を単一のnat
タイプに簡単に置き換えることができたとしても、 isであっても、浮動小数点数の型を明確にします。実数はプログラミング言語には存在できないため、これらは実数ではありません。これらもまた、かなり有理数ではありません(必要に応じて有理型を作成するのは簡単ですが)。基本的に、IEEEは、実数をある程度近似する方法を決定しました。それ以来、すべての言語(およびプログラマー)は、それらに悩まされてきました。
最後に:
おそらく、プログラマーは誰かが特定のサイズよりも大きい数を使用できるようにしたくないので、これによりユーザーはそれを制限できます。
これは正当な理由ではありません。まず、型が数値の境界を自然にエンコードできる状況は考えられません。言うまでもなく、プログラマーが強制したい境界がプリミティブ型のサイズに正確に対応する可能性は天文学的に低いです。
その理由は非常に簡単です:効率。複数の方法で。
ネイティブデータタイプ:言語のデータタイプがハードウェアの基本的なデータタイプと一致するほど、言語の効率は高くなります。 (プログラムが必ずしも効率的であるという意味ではありませんが、実際に何をしているのかがわかっていれば、ハードウェアが実行できるのと同じくらい効率的に実行されるコードを書くことができるという意味です。)提供されるデータ型Javaは、世の中で最も人気のあるハードウェアのバイト、ワード、ダブルワード、クワッドワードに対応しています。これが最も効率的な方法です。
2ビットシステムでの不当なオーバーヘッド:すべてを64ビット長の固定サイズにマップすることを決定した場合、これはかなり多くを必要とする32ビットアーキテクチャに大きなペナルティを課すことになります。 32ビット動作よりも64ビット動作を実行するためのクロックサイクル。
メモリの浪費:そこには、メモリの調整にあまり注意を払っていないハードウェアがたくさんあります(Intel x86およびx64アーキテクチャがその例です)。そのため、そのハードウェア上の100バイトの配列は、 100バイトのメモリのみを使用します。ただし、バイトがなく、代わりにlongを使用する必要がある場合は、同じ配列が1桁多いメモリを占有します。そして、バイト配列は非常に一般的です。
数値のサイズの計算:渡された数値の大きさに応じて整数のサイズを動的に決定するというあなたの考えは単純すぎます。数字を「渡す」という単一のポイントはありません。実行時に、より大きなサイズの結果が必要になる可能性があるすべての操作で、数値の大きさの計算を実行する必要があります。数値をインクリメントするたびに、2つの数値を追加するたびに、2を乗算するたびに番号など.
異なるサイズの数の操作:その後、潜在的に異なるサイズの数がメモリ内に浮かぶと、複雑になりますall演算:2つの数値を単純に比較する場合でも、ランタイムは最初に、比較する両方の数値が同じサイズかどうかを確認し、そうでない場合は、大きい方のサイズに合わせて小さい方のサイズを変更する必要があります。
特定のオペランドサイズを必要とする演算:特定のビット単位演算は、特定のサイズの整数に依存しています。事前に決められた特定のサイズがないため、これらの操作をエミュレートする必要があります。
ポリモーフィズムのオーバーヘッド:実行時に数値のサイズを変更するということは、本質的にポリモーフィックでなければならないことを意味します。これは、スタックに割り当てられた固定サイズのプリミティブになることはできず、ヒープに割り当てられたオブジェクトでなければならないことを意味します。それはひどく非効率的です。 (上記の#1をもう一度読んでください。)
他の回答で説明されている点が繰り返されないように、代わりに複数の視点の概要を説明します。
これは、Javaの歴史に関するWikipediaの記事ですでに説明されており、 Marco13の回答 でも簡単に説明されています。
私はそれを指摘します:
効率が問題になるのはいつですか?
ストレージの効率(メモリ内またはディスク上)
実行効率(CPU内、またはCPUとメモリ間)
相互運用性
char
配列を受け入れるように指定する必要がある場合があります。 (Example。)BitConverter
など)を提供することを選択します。文字列処理
ファイル形式の処理
次のシナリオを検討してください。
多くの場合、何桁も安全にスケールアップできるソフトウェアは、その目的のために設計されなければならず、複雑さが増しています。整数オーバーフローの問題が解消されても、自動的には提供されません。これは、言語設計の観点に答える完全な円になります。多くの場合、意図しない整数のオーバーフローが発生したときに(エラーまたは例外をスローすることによって)作業の実行を拒否するソフトウェアは、天文学的に大きな演算に自動的に準拠するソフトウェアよりも優れています。
これはOPの視点を意味し、
プリミティブ値に使用するバイト数を設定する必要があるのはなぜですか?
不正解です。プログラマーは、整数値が可能な最大値magnitudeを指定することを許可され、必要になる場合もあります。ソフトウェアの重要な部分を取り上げます。 庭先の答え が指摘しているように、プリミティブ型によって課される自然な制限はこの目的には役立ちません。言語は、プログラマーに大きさを宣言し、そのような制限を強制する方法を提供する必要があります。
それはすべてハードウェアから来ています。
1バイトは、ほとんどのハードウェアでアドレス可能な最小のメモリ単位です。
今述べたすべての型は、複数のバイトから構築されています。
1バイトは8ビットです。これにより、8つのブール値を表現できますが、一度に1つだけを調べることはできません。あなたは1に対応し、すべて8に対応します。
以前はそれほど単純でしたが、8ビットバスから16、32、そして現在は64ビットバスに移行しました。
つまり、バイトレベルでアドレス指定できる間は、隣接するバイトを取得せずにメモリから1バイトを取得することはできません。
このハードウェアに直面すると、言語設計者は、ハードウェアに適合するタイプを選択できるタイプを選択できるようにすることを選択しました。
そのような詳細は、特に任意のハードウェアでの実行を目的とする言語では抽象化でき、抽象化する必要があると主張できます。これにはパフォーマンスの問題が隠されていますが、それは正しいかもしれません。それはそのようには起こらなかっただけです。
Javaは実際にこれを試みます。バイトは自動的にIntに昇格されます。あなたがその中で深刻なビットシフト作業を最初にしようとするときにあなたを狂わせるという事実。
では、なぜうまくいかなかったのでしょうか。
Javaの大きなセールスポイントは、よく知られている優れたCアルゴリズムに腰を下ろし、Javaでタイプアップし、小さな調整を加えるだけで機能するということです。 Cはハードウェアに非常に近いです。
それを維持し、整数型からサイズを抽象化しても、一緒に機能しませんでした。
だから彼らは持つことができた。彼らはちょうどしませんでした。
おそらく、プログラマーは誰かが特定のサイズよりも大きい数を使用できるようにしたくないので、これによりユーザーはそれを制限できます。
これは正当な考え方です。これを行う方法があります。 クランプ機能 1。言語は、それらの型に任意の境界を焼き付けることもできます。そして、それらの境界がコンパイル時にわかっている場合、それらの数値の格納方法を最適化することができます。
Javaはその言語ではありません。
おそらく、これらの型がJavaに存在する重要な理由の1つは、単純であり、技術的ではないことです。
CおよびC++にもこれらのタイプがありました!
これが理由であるという証拠を提供することは困難ですが、少なくともいくつかの強力な証拠があります。 Oak Language 仕様(バージョン0.2)には次の文章が含まれています。
.1整数型
Oak言語の整数はCとC++の整数に似ていますが、2つの例外があります。すべての整数型はマシンに依存せず、Cが導入されてからの世界の変化を反映するように従来の定義の一部が変更されました。 4つの整数型の幅は8、16、32、および64ビットであり、
unsigned
修飾子が前に付いていない限り、署名されます。
したがって、問題は次のように要約できます。
ここで尋ねられた質問に関して、手紙の質問への回答が満足できるものかどうかはわかりません。しかし、ここでの他の回答と組み合わせると、(Javaでの存在がC/C++からのレガシーだけであるかどうかに関係なく)これらのタイプを持つことが有益であることが明らかになる場合があります。
私が考えることができる最も重要な理由は
バイトは、アドレス指定可能な最小のメモリユニットです(CandiedOrangeについては既に説明しています)。 byte
は、ファイルから、またはネットワーク経由で読み取ることができるデータの基本的なビルディングブロックです。 いくつかこれの明示的な表現が存在する必要があります(ほとんどの言語では、偽装されている場合でも存在します)。
実際には、すべてのフィールドとローカル変数を単一の型を使用して表現し、この型をint
と呼ぶことは理にかなっています。これについては、stackoverflowに関連する質問があります: Java APIがshortまたはbyteではなくintを使用するのはなぜですか? 。そこの私の回答で述べたように、より小さな型(byte
およびshort
)を持つ理由の1つは、これらの型の arrays を作成できることです。 :Javaには、まだ「ハードウェアに近い」配列の表現があります。他の言語(およびInteger[n]
配列などのオブジェクトの配列とは対照的)とは対照的に、int[n]
配列は、値がヒープ全体に分散している参照のコレクションではありません。代わりにwillは実際にはn*4
バイトの連続したブロック-既知のサイズとデータレイアウトを持つ1つのメモリチャンクです。 1000バイトを任意のサイズの整数値オブジェクトのコレクション、またはbyte[1000]
(1000バイトを必要とする)に格納することを選択した場合、後者は実際にメモリを節約する可能性があります。 (これの他のいくつかの利点はより微妙である可能性があり、ネイティブライブラリとJavaをインターフェイスするときにのみ明らかになります)
具体的にお伺いした点について:
渡された数値の大きさに応じて、サイズを動的に決定することはできませんか?
データのサイズを動的に設定するには、動的に変更できる必要があることを意味します。これはパフォーマンスの問題を引き起こす可能性がありますか?
まったく新しいプログラミング言語をゼロから設計することを検討した場合、変数のサイズを動的に設定することはおそらく可能です。私はコンパイラー構築の専門家ではありませんが、特にstrongly型付き言語がある場合、動的に変化する型のコレクションを賢明にマンガ化するのは難しいと思います。したがって、おそらく「汎用の任意精度の数値データ型」に格納されているすべての数値に要約されますが、これは確かにパフォーマンスに影響を与えます。もちろん、強く型付けされたプログラミング言語や任意のサイズの数値型を提供するプログラミング言語はありますが、このように進んだ実際の汎用プログラミング言語はないと思います。
サイドノート:
Oak仕様で言及されているunsigned
修飾子について疑問に思ったかもしれません。事実、 "unsigned
はまだ実装されていないため、実装されていない可能性もあります。" 。そして彼らは正しかった。
C/C++がこれらの異なる整数型を使用した理由に加えて、int
のビット数がわからないほどひどく混乱した理由に疑問を感じるかもしれません。この理由は通常、パフォーマンスに関連しており、他の場所で調べることができます。
いくつかの理由があります
(1)1バイト変数と1ロングの格納は重要ではありませんが、配列内の数百万の格納は非常に重要です。
(2)特定の整数サイズに基づく「ハードウェアネイティブ」演算は、はるかに効率的であり、一部のプラットフォームの一部のアルゴリズムでは、それが重要になる場合があります。
確かに、パフォーマンスとアーキテクチャについてまだ教えていないことを示しています。
データサイズの重要性を無視すると、常にパフォーマンスに影響します。必要なだけ多くのリソースを使用する必要がありますが、それ以上は常に使用する必要はありません。
これは、本当に単純なことを行うプログラムやシステムと、多くのリソースを必要とする非常に非効率的であり、そのシステムの使用を非常にコストがかかるものとの違いです。または、多くのことを実行しますが、他のシステムよりも速く実行され、実行するのに本当に安価なシステムです。