
Javaのintとは?Javaの整数型のスタンダード「int」を解説
Javaのintは、プリミティブ型の一つで、32ビットの範囲で整数を表現できます。intでは、およそ±21億5千万の値を表現できます。
Javaで整数を扱う時は、普通はintを使います。intを使わないJavaのプログラムは考えられないほど、ごく普通に使われます。ですので、intを使うことは、Javaプログラミングの基本だとも言えるでしょう。
だからこそ、intの特徴や使い方をしっかり知っているということは、Javaでプログラミングをする上でもはっきりとした足場や土台があるということです。それくらいJavaプログラミングではintは重要です。
この記事ではintについて、そもそもintとはどういうものか、intはどうやって使うのか、気を付けたい所などを初心者向けにお伝えします。
※この記事のサンプルは、Java 11の環境で動作確認しています
1.intはJavaの整数型のスタンダード
intはJavaで最もよく使う整数のデータ型で、プリミティブ型(primitive、基本データ型)と呼ばれるものの一つです。
一つのintのサイズは32ビット(4バイト)です。intという名前の由来は、英語のinteger(整数)の先頭部分です。
intを他のプリミティブ型と簡単に比較すると、以下のとおりです。
データ型 | 値の種類 | ビット数 | 表現できる値の範囲 | 接尾語 | 備考 |
boolean | 真偽値 | 1ビット | true/falseのどちらか | – | |
byte | 整数 | 8ビット(1バイト) | -128~127 | – | |
short | 整数 | 16ビット(2バイト) | -32,768~32,767 | – | |
char | 文字 | 16ビット(2バイト) | 0~65,535 | – | Unicode文字、\u0000~\uffff |
int | 整数 | 32ビット(4バイト) | -2,147,483,648~ 2,147,483,647 | – | 約±21億4千7百万、Unicodeコードポイント |
long | 整数 | 64ビット(8バイト) | -9,223,372,036,854,775,808~ 9,223,372,036,854,775,807 | Lまたはl | 約±922京 |
float | 浮動小数点 | 32ビット(4バイト) | ±3.40282347E+38~ 1.40239846E-45 | Fまたはf | 単精度 約±3.4×10の38乗~約±1.4×10の-45乗 |
double | 浮動小数点 | 64ビット(8バイト) | ±1.79769313486231570E+308~±4.94065645841246544E-324 | Dまたはd | 倍精度 |
1-1.intは1ずつ増減し、プラスマイナスがある整数型
整数型とは、プログラム上で数字を表現するデータ型の一つです。整数は-1、0、1、2、3…のように、ちょうど1ずつ増減する数値で、小数点は含みません。小数点の数値を含められるデータ型は、浮動小数点型という別のものです。
Javaではプラスの整数、マイナスの整数の両方をintだけで表現します。他のプログラミング言語では、符号のないデータ型がありますが(unsigned intなど)、Javaのintは符号があるデータ型です。
1-2.intは±21億ぐらいまでの整数を表現できる
Javaのintでは、±21億5千万くらいまでの整数を表現できます。正確に書けば、-2,147,483,648~2,147,483,647の範囲の整数です。
32ビットの2進数で表現できるパターンは約43億とおりなので、少ないと思われたかもしれません。でも、Javaでのintはもっとも大きな桁の1ビットをプラスマイナスの符号に使いますので、数値に使えるサイズは31ビット(約21億)になるのです。
intの32ビットのビットパターンと、実際の数字は以下のように対応します。
8バイト目 | 7~2バイト目 | 1バイト目 | 10進数表現 | 備考 |
0000 | 全て0 | 0000 | 0 | すべてのビットが0 |
0000 | 全て0 | 0001 | 1 | |
0000 | 全て0 | 0010 | 2 | |
… | ||||
0111 | 全て1 | 1110 | 2,147,483,646 | |
0111 | 全て1 | 1111 | 2,147,483,647 | Integer.MAX_VALUE |
1000 | 全て0 | 0000 | -2,147,483,648 | Integer.MIN_VALUE |
1000 | 全て0 | 0001 | -2,147,483,647 | |
… | ||||
1111 | 全て1 | 1110 | -2 | |
1111 | 全て1 | 1111 | -1 | すべてのビットが1、+1すると0に戻る |
intは0から始まって1ずつ大きくなり、32ビット目が0かつ1~31ビット目が全て1が最も大きな正の整数です。そこから、次の32ビット目が1かつ0~31ビット目が全て0が最も小さな負の整数で、1ずつ大きくなっていき、全てのビットが1になると-1です。
1-3.Javaのintはいつでもどこでも32ビット
Javaではどの環境でも、昔も今もこれからも、intは32ビットです。「それが何なの? 当然でしょ?」と思われたかもしれませんが、実はこれがJavaのいいところの一つです。
他のプログラミング言語では、このような基本的なデータの大きさにも、環境による違いがあったりします。例えば、昔のC言語ではintは16ビットでしたが、今では大体32ビットです。さらに、今ではint16/int32/int64などがあり、きちんと理解して使うにはなかなか複雑です。
ですが、Javaでは最初からintは32ビットです。これは今後もずっと変わりません。どんなJavaの環境でも同じですし、プログラムの作り直しはいらないのです。覚えやすさと言う意味でもいいことですよね。
2.intの基本的な使い方
ここでは、intの使い方のサンプルをお伝えします。そして、intを使う上では注意すべきことがいくつかありますので、それらも順番にお伝えします。
2-1.intの宣言の仕方、初期値
2-1-1.intの変数・配列の宣言、初期値の指定
intは型の一つですから、以下のように変数や配列変数の型として使えます。もちろんメソッドの引数や、戻り値としても使えます。
// int型の変数の宣言と、初期値の代入 int i = 65535; // int型の配列の宣言と、各インデックスへの値の代入 int[] iarr = new int[5]; iarr[0] = 11; iarr[1] = 12; iarr[2] = 13; iarr[3] = 14; iarr[4] = 15;
2-1-2.intのリテラルの書き方
小数点の無い数字をプログラム上にそのまま書くと、Javaのプログラム上ではintとして認識されます。このような書き方をリテラル(文字どおりの、という意味)と呼びます。
intのリテラルを書く時は、接頭語をつけなければ10進数です。Java 11の時点では10進数の他に以下の書き方ができます。さらに、“_”で桁区切りが出来るようにもなっています。
- 2進数(“0b”で始まる、Java 7から)
- 8進数(“0”で始まる)
- 10進数(接頭語なし)
- 16進数(“0x”で始まる)
int i1 = 123456789; // 10進数 int i2 = 0b1010101; // 2進数 int i3 = 012345670; // 8進数 int i4 = 0x123ABCDE; // 16進数 int i5 = 123_456_789; // 桁区切り(3桁ごと) int i6 = 0b1111_1010_0101; // 桁区切り(4ビットごと)
2-1-3.intのフィールド、配列の初期値は0
フィールドとしてint型の変数を使ったり、int型の配列を使う場合は、未初期化だと0になります。
class IntTest { int i; // intをフィールドとして宣言したが、初期値は未設定 public static void main(String[] args) { IntTest intTest = new IntTest(); System.out.println(intTest.i); // → 0、変数宣言時に初期化されていないのでデフォルト値が設定される int[] array = new int[5]; System.out.println(array[0]); // → 0、newした時点で配列全体が0で初期化される } }
2-2.intを使った四則演算
intを使った四則演算(加減乗除)には、四則演算用の算術演算子、つまり+-*/を使います。余りを求めるなら%です。ですので、ごく直感的に計算できるかと思います。
ただし、計算結果で小数点が出た場合、intの変数へ代入すると、小数点以下の値は切り捨てされます。これは整数型のプリミティブに共通する動きです。
int i = 10; int j = 3; int plus = i + j; int minus = i - j; int multiply = i * j; int divide = i / j; int surplus = i % j; System.out.println(plus); // → 13 System.out.println(minus); // → 7 System.out.println(multiply); // → 300 System.out.println(divide); // → 3(計算上は10 / 3 = 3.333…だが、小数点以下が切り捨てられて0になる) System.out.println(surplus); // → 1
2-1-1.大きな整数を計算するなら、符号の変化に要注意!!
絶対値が大きな値を使う場合は、符号の変化に注意しましょう。
intに数値を加減算して、プラス側の最大値を超えるとマイナス側の最大値になり、マイナス側の最小値よりも小さくなるとプラス側の最大値になります。つまり、プラスマイナスがひっくり返るタイミングがあり、オーバーフローと呼ばれます。
int i1 = Integer.MAX_VALUE; int i2 = i1 + 1; int i3 = i1 + 2; System.out.println(i1); // → 2147483647 System.out.println(i2); // → -2147483648 System.out.println(i3); // → -2147483647 int i4 = Integer.MIN_VALUE; int i5 = i4 - 1; int i6 = i4 - 2; System.out.println(i4); // → -2147483648 System.out.println(i5); // → 2147483647 System.out.println(i6); // → 2147483646
これは、二進数で言うと 0111111111111111111111111111111 と 1000000000000000000000000000000 の間でプラスマイナスが変わるということです。前者がInteger.MAX_VALUE、後者がInteger.MIN_VALUEです。これを細かく言えば、Javaでは2の補数でマイナスの数値を表現しているから、ということです。
問題になる例として、業務システムで金額項目をintにして、その金額同士を足し合わせると予期せぬ数値になることがあります。例えば、15億と10億を足しても25億にはならないのです。計算する数値の桁数が大きいなら、もっと大きな数が扱えるlongやdoubleにしておくといいでしょう。
int i1 = 1_000_000_000; // 10億 int i2 = 1_500_000_000; // 15億 int sum = i1 + i2; System.out.println(sum); // → -1794967296! 25億にはならない!
2-3.intでのビット演算
intはビット演算にも使えます。業務向けのプログラムではビット演算はほとんど使いませんが、科学技術計算など処理速度が要求されるプログラムでは、ビット演算により計算をすることもあります。特に、掛け算や割り算をシフトで代用するのが代表的です。
なお、C言語の名残があるプログラムだと、処理のオプションをビットパターンで指定することがあったりします。また、プログラムの仕様として特定ビットの値が1かどうかを調べることもありますので、ビット演算のやり方は覚えておいても損はありません。
以下では、Javaでintを使ったビット演算のやり方だけ、簡単に示しておきます。
int i1 = 0b101; int i2 = 0b010; // 論理和(or) | int or = i1 | i2; System.out.println(Integer.toBinaryString(or)); // → 111 // 論理積(and) & int and = i1 & i2; System.out.println(Integer.toBinaryString(and)); // → 0 i1 = 0b1100; i2 = 0b1010; // 排他的論理和(xor) ^ int xor = i1 ^ i2; System.out.println(Integer.toBinaryString(xor)); // → 110 i1 = 0xfffffffe; // 論理否定(not) ~ int not = ~i1; System.out.println(Integer.toBinaryString(not)); // → 1 i1 = 0b11111111111111111111111111111111; // 左シフト << int ls = i1 << 1; System.out.println(Integer.toBinaryString(ls)); // → 11111111111111111111111111111110 // 右シフト(算術シフト) >> int rs1 = i1 >> 1; System.out.println(Integer.toBinaryString(rs1)); // → 11111111111111111111111111111111 // 右シフト(符号なし、論理シフト) >>> int rs2 = i1 >>> 1; System.out.println(Integer.toBinaryString(rs2)); // → 1111111111111111111111111111111
2-4.intと他のプリミティブ型との変換
Javaでは数値型のプリミティブ型として、intとビット数が違う整数(byte/short/char/long)と、小数点が扱えるもの(float/double)があります。それらとの変換や演算の時には注意することがいくつかあります。
2-4-1.整数のプリミティブ型との変換
ビット数が違うものとの変換では、ビット数が小さい型→大きい型への変換は問題ありません。intなら、byte/short/charからの変換です。ビット数が小さい型の値は、ビット数が大きい方の型で全て表現できるからです。
でも、ビット数が大きい型→小さい型へ変換する場合は、大きい型の上位ビットにある情報が消えてしまいます。intならlongからの変換が要注意です。
また、ビット数が大きい型から小さい型に変換する際は、明示的なキャストが必要です。これは、値が失われる可能性がある操作をしているのだと、プログラマに意識させるためです。
ビット数が小さい型から大きな型へはキャストはいりませんが、ここでも暗黙的キャストというものが行われています。
byte b = -128; short s = -32768; char c = 65535; long l = -9223372036854775808L; // 0b100000...のように、1の後ろに0が63個続く int i1 = b; int i2 = s; int i3 = c; int i4 = (int)l; // 64ビットのlongから32ビットのintへの変換にはキャストが必要 System.out.println(i1); // → -128 System.out.println(i2); // → -32768 System.out.println(i3); // → -65535 System.out.println(i4); // → 0! longの33~64ビット目がなくなり、全て0になったため
逆に、intから変換する場合は、それぞれのプリミティブのビット数までの値になります。longならintよりも大きいので、値をそのまま保持できます。
下記の例だと、byte/short/charはそれぞれのビットが全て1になります。ビットが全て1だと、整数型なら共通で-1です。
int i = 0b11111111111111111; // 1~17ビット目まで1とする。そこから上は0 System.out.println(i); // → 131071 byte b = (byte)i; short s = (short)i; char c = (char)i; long l = i; // ビット数がより多いlongへならキャストはしなくてもいい System.out.println(b); // → -1、0b11111111 System.out.println(s); // → -1、0b1111111111111111 System.out.println((int)c); // → 65535、0b1111111111111111 System.out.println(l); // → 131071、longならカバーできる
2-4-2.浮動小数点のプリミティブ型との変換
浮動小数点のプリミティブ(float/double)とintの間で変換をすると、浮動小数点数にはあった小数部が失われてしまいます。また、浮動小数点はintよりもはるかに桁が大きな数値を表現できますが、そういう大きな桁の情報もなくなってしまいます。
float/doubleからintに変換する際は、値が失われる可能性がある操作なので、明示的なキャストが必要です。逆に、intからfloat/doubleへの変換にはキャストは不要です。
double d1 = 1.23; double d2 = Double.MAX_VALUE; // 1.7976931348623157E308、約1.8×10の308乗!! int i1 = (int)d1; int i2 = (int)d2; System.out.println(i1); // → 1 System.out.println(i2); // → 2147483647、約2.15×10の9乗
int i = Integer.MAX_VALUE; System.out.println(i); // → 2147483648 float f = i; double d = i; System.out.println(f); // → 2.14748365E9 System.out.println(d); // → 2.147483647E9
2-5.intとStringとの変換
Stringもintと並んでJavaでは重要なクラスです。Stringをintにすること、またintをStringにすることは、プログラムでは日常茶飯事です。ここではその方法をお伝えします。
2-5-1.String→intの変換
ファイルから読み込んだ文字列や、引数で受け取った文字列からintを作りたい時があります。そういう時はInteger.parseIntを使いましょう。文字列をintに変換してくれます。intと解釈できない文字列の場合はNumberFormatExceptionがthrowされますのでご注意を。
String intStr = "12345"; int i = Integer.parseInt(intStr); System.out.println(i); // → 12345
2-5-2.int→Stringの変換
逆に、intをStringにしたい時があります。簡単に行うなら、Integer.toString(int)か、String.valueOf(int)を使いましょう。ちなみに、どちらを使っても結果は同じです。
int i = 12345; String intStr = String.valueOf(i); System.out.println(intStr); // → "12345"
また、intを3桁区切りなどでフォーマットしたり、0埋めをしたい場合もあるでしょう。その場合は、java.text.DecimalFormatやString.formatを使います。それぞれの書式の詳細は、Javadocを参照してください。
int i = 1234567; DecimalFormat df1 = new DecimalFormat("#,###"); DecimalFormat df2 = new DecimalFormat("000,000,000"); String intStr1 = df1.format(i); String intStr2 = df2.format(i); System.out.println(intStr1); // → 1,234,567 System.out.println(intStr2); // → 001,234,567
int i = 1234567; String intStr1 = String.format("%,d", i); String intStr2 = String.format("%,011d", i); System.out.println(intStr1); // → 1,234,567 System.out.println(intStr2); // → 001,234,567
java.text.DecimalFormat
https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/text/DecimalFormat.html
java.lang.String.format
3.intとInteger
Javaでは32ビットの整数を表現するために、intとIntegerの二つの方法があります。Javaプログラミングの初心者は、なぜ同じ整数の表し方が二つあるのか混乱すると思います。
この章ではその理由と、intとIntegerの使い分けの方針などをお伝えします。
3-1.二種類の表現方法は性能確保のため
Javaではプリミティブ型のintと、クラスのIntegerは別物です。C#などではこういう区別がないのに、なぜJavaではあるのか。これは、Javaが生まれた当時にプログラムの実行速度を確保するためでした。
Javaは1995年に登場したプログラミング言語です。当時のCPUのクロック周波数は今とは桁が違い、一般向けのCPUでようやく100MHzを超えたくらい。メモリの量も全体で数MB~数10MBと非常に乏しかったものです。
intは32ビットの数字そのものなので、楽に速く扱えます。しかし、intをクラスとすると、一つのintの数値を表すのに32ビットよりもずっと多くのメモリを使います。簡単な計算をするにも、処理上では余分なオーバーヘッドが発生します。
3-2.クラスのIntegerならnullを表現できる
JavaでIntegerを使うのは、Integerが持つメソッドを使いたい時と、値がない場合…すなわちnullを表現したい時です。例えば、SQLでは値の有り無しをNULLかどうかで表現できますが、それをJavaのintでは上手に表現できません。
ですから、プログラム上では0や-1や999などの値に特別な意味を持たせたり、Integer.MAX_VALUEやMIN_VALUEを使うのですが、確実さには欠けます。そういう値のチェックを忘れるなどのミスもしがちです。
そういう時に参照型であるIntegerを使えば、値がないことをnullとして表現できるのです。Integerをどういう時に使うか分からない方は、その変数でnullを表現する必要があるかを一つの指針にしてみてください。
3-3.オートボクシングでintとIntegerを自動変換する
Java 1.5でオートボクシング(auto boxing)という仕組みが導入されました。オートボクシングで、intとIntegerをプログラム上でほぼ同じものとして扱えます。
プログラム上でintを使う所ではIntegerを使えますし、Integerを使う所ではintが使えます。本当のプログラム上は相変わらずintとIntegerは別物なのですが、その違いをJavaが裏で自動的に変換をしてくれるのです。
Integer intObj = Integer.valueOf("12345"); // Integer int intPri = 65535; // int intPri = intObj; // → 12345、intにIntegerを代入できる intPri = new Integer("65535"); // → 65535、同上 intObj = 12345; // → 12345、Integerにintを代入できる
これでJavaの面倒な部分がある程度解消されました。ですが、前述のとおりIntegerはnullを表せますが、intは必ず何かの整数なので、nullに相当するものがありません。
ですので、以下のように予期せぬところでNullPointerExceptionが発生したりします。これは2019年のJava 11の時点でも変わっていません。プログラマが注意するか、Optionalを使う必要があります。
class IntTest { static Integer returnInteger() { return null; } public static void main(String[] args) { int i = returnInteger(); // → nullをintに変換できないので、実行するとNullPointerExceptionが発生する!! } }
3-4.【応用】符号なしintとしての扱い方
この記事ではずっと、intはプラスマイナスがある32ビットの整数だとお伝えしてきました。それは今も変わりませんが、Java 8でintを符号なし整数として扱えるメソッドがIntegerに追加されました。それらのメソッドを使うと、intの32ビットをフルに使った計算ができます。
例えば以下のようにです。比較しているのは同じintでビットパターンも変わりませんが、compareUnsignedでは32ビット目も含めた整数として比較しているのが分かるでしょうか。
int i1 = Integer.MAX_VALUE; // → 0b01111111111111111111111111111111 int i2 = Integer.MIN_VALUE; // → 0b10000000000000000000000000000000 int cmp1 = Integer.compare(i1, i2); int cmp2 = Integer.compareUnsigned(i1, i2); System.out.println(cmp1); // → 1(i1の方が大きい)、i1はプラスなのでi2よりも大きい System.out.println(cmp2); // → -1(i2の方が大きい)、32ビットをフルに使っているため、i2の方が大きいと判断される
Integer.~Unsigned系のメソッドは以下のものです。それぞれの詳細は、Javadocを参照してみてください。
Integer.toUnsignedString:符号なし整数として文字列化する
Integer.parseUnsignedInt:符号なし整数としてパースする
Integer.toUnsignedLong:符号なし整数としてlongに変換する
Integer.divideUnsigned:符号なし整数として割り算する
Integer.remainderUnsigned:符号なし整数として余りを求める
4.まとめ
この記事では、Javaのintについてお伝えしてきました。Javaのintは32ビットの整数で、±21億くらいまでの値を表現できます。intはJavaで整数を使う時の標準ともいえるデータ型です。
Javaでは、整数を表現するにはプリミティブ型のintと、クラス(参照型)としてのIntegerの二種類があり、それらは違うものであることには注意しましょう。ただ、オートボクシングにより違いが見えにくくはなっています。
さて、整数はどんなプログラミング言語にもあるデータ型です。ですので、Javaで整数の使い方を学んでおけば、他のプログラミング言語でもそう大きな違いなく使えるでしょう。
特にビットパターンと数値の対応を覚えておけば、いざという時に役に立つかもしれません。それに、ビットパターンがイメージできれば、コンピュータの世界により深く入り込めるようになります。これを機に、頑張ってみませんか?
コメント