演算子

since: Sep./9th/2002; revised: Dec./25th/2001

以前、文字列連結の演算子として + を紹介した。この演算子は、演算子の両辺に整数型、浮動小数点数型のデータが存在すれば、単なる足し算になる。

本節では、 Java で登場する基本的な演算子を網羅的に紹介する。具体的な使い方は、以降のサンプルの中で説明しよう。本稿は、必要があれば参照する程度でよい。

基本的な算術演算子

算術演算子
足し算 + 加法演算子
引き算 -
掛け算 * 乗法演算子
割り算 /
余り %

算術演算子は、加法演算子と乗法演算子に大別されます。乗法演算子のほうが優先順位が高く、同じ優先順位ならば、左から順に評価されます。優先順位を明示するときはカッコ "(", ")" を使います。基本的には通常の算数どおりと思っていて良いでしょう。中カッコや大カッコは存在しません。

モジュロ演算子 % は使い道が分からないかもしれませんが、例えば偶数/奇数の判定なら 2 で割った余りが 0 か 1 かで区別できます。同様に、4つの場合分けなら 4 で割った余りが 0, 1, 2, 3 の何れかで判別できるので、実際はかなり便利です。

モジュロ演算子は、負数に対する場合や小数に対する場合などに単なる商と余りとはことなる面がありますが、ここでは説明しません。

以上の演算子は、基本的には通常の算術演算に従うのですが、プログラミング言語の場合は、変数の型によって格納できる数に限りがあります。このとき、桁あふれ(オーバーフロー)の発生が考えられます。例えば、int型には32ビットの整数が格納可能ですが、小数は切り捨てられます。一方、long型には64ビットの整数が格納可能です。このとき、例えば、次の演算を考えて見ます。

int a = 10000;
int b = 1000000;
int c;
c = a / b * b;

通常の算術演算に従えば、c は a に等しく、 10,000になるはずです。しかし、結果は 0 になります。左から順に演算されるので、最初に a / b が演算されたときに小数点以下が切り捨てられて 0 になってしまうからです。これを回避するためには、次のようにすべきです。

c = a * b / b;

これでも結果は悲惨な事になります。int型に格納できる整数の最大値は231 - 1 = 2,147,483,647 です。a * b が計算された 10,000,000,000 よりも小さいので、正しい数が格納されません。下位32ビットが切り取られて、 a * b の値は 1410065408 となり、これを b で割った最終的な c は 1410 になります。

この問題を解決するには、int 型ではなく、 long 型を使うほかありません。内部で表現可能なサイズを大きくするのです。

long a = 10000;
long b = 1000000;
long c = a * b / b;

これで正しい結果が得られました。桁あふれを未然に防ぐ処方箋はありませんが、一般に割ってから掛けるよりも、掛けてから割ったほうが正確な値が得られる可能性が高まります。正確な値が得られるかどうかは内部表現のサイズに依存します。

比較演算子

比較演算子
小なり<
大なり>
小なりイコール<=
大なりイコール>=
等しい==
等しくない!=

true/false が返ってくる条件式の、単純なものを記述するときに不可欠なものです。算術演算子と共に必ず覚える必要があります。

「等しい」は = ではありません。 == です。 = は、右辺を左辺に代入する演算子です: x = 10;

また、文字列の比較には == は不適切な場合が多いでしょう。メソッド equals() を使いましょう。

浮動小数点数の特殊な値として、非数 NaN、正の無限大 POSITIVE_INFINITY、負の無限大 NEGATIVE_INFINITY を紹介しました。二つの無限大は float 型、double 型と比較可能です。

double d = 1.0/0;
boolean bln = (d == Double.POSITIVE_INFINITY);	// bln には true が代入される

但し、負数の平方根などで返される NaN は比較できません。NaN と比較した全ての比較式はおしなべて false を返します。

x < Float.NaN;
x > Float.NaN;
x <= Float.NaN;
x >= Float.NaN;
x == Float.NaN;
x != Float.NaN;    // x の値によらず全て false が返る

x が NaN かどうか調べるためには、 isNaN() を使います。

boolean bln = Float.isNaN(x); 	// float 型変数 x の値が NaN ならば true
        bln = Double.isNaN(y);	// double 型変数 y の値が NaN ならば true

論理演算子

論理演算子
論理積(かつ) AND&
論理和(又は) OR|
排他的論理和 XOR^
否定!(条件)

論理演算子は、 true/false で返ってくる条件式の、複雑な場合を表現するときに、とても便利です。是非使えるようになりましょう。

例えば、 (a < b) & (c < a) の場合、 c < a < b であれば true が返ってきます。

真:○, 偽:×
aba & ba | ba ^ b!a
× ×
× × ×
× ×
×× × × ×

& は両方とも真である場合を除いて偽、 | は両方とも偽である場合を除いて真。

例えば、この表を一行目から読むと、

    a = true, b = trueのとき、
    a & b -> true, a | b -> true, a ^ b -> false, !a -> false

二行目は、

    a = true, b = falseのとき、
    a & b -> false, a | b -> true, a ^ b -> true, !a -> false

a, b は次のいずれかの場合が考えられます。

a = false;
b = true;
System.out.println(a & b);
System.out.println(a | b);
System.out.println(a ^ b);
System.out.println(! a);
System.out.println((3 < 0) & (10 != 10));
System.out.println((3 < 0) | (10 != 10));
System.out.println((3 < 0) ^ (10 != 10));
System.out.println(!(3 < 0));

もともとは 0, 1 のビット演算でもあります。

0110 1100 & 1010 1110

この演算式は次のように計算されます:

     AND              OR               XOR
  0110 1100        0110 1100        0110 1100
  1010 1110        1010 1110        1010 1110
&-----------      |----------      ^----------
  0010 1100        1110 1110        1100 0010

ショートサーキット演算子

条件式に対する二項演算では、通常は次のショートサーキット演算子(短絡演算子)を使います。

ショートサーキット演算子
論理積(かつ)&&
論理和(又は)||

a&&b なら、両方とも真である場合を除いて偽なので、最初に a が評価され、偽であれば、 b の評価はせずに、偽を返します。 a が真である場合に限り b が評価されます。

a||b なら、両方とも偽である場合を除いて真なので、最初に a が評価され、真であれば、 b の評価はせずに、真を返します。 a が偽である場合に限り b が評価されます。

ショートサーキット演算子の場合は、演算子の左辺の演算対象の真偽によって、右辺の演算対象が評価されません。したがって、右辺に値が変化するような式を含めておくと、その後の値が状況によって変わってしまうことに注意が必要です。

次の場合を考えてみましょう:

初期状態a = 1, i = 1a = 2, i = 1
a == 1 | i++ < 5;i = 2i = 2
a == 1 || i++ < 5;i = 1i = 2
結果truetrue

ショートサーキット演算子の場合は、演算子の左辺の項が真であれば、右辺の評価はしないので、 i++ は無視されて i の値は変わりません。

ビット操作演算子

符号付 n ビット右シフト>>n 2のn乗の切り捨て除算と同じ
0 埋め n ビット右シフト>>>n
n ビット左シフト<<n 2のn乗の乗算と同じ
ビット反転~

ビット列を右や左にシフトします。二進数でビット列をずらすと言うのは、2 を掛けたり割ったりすることと同じです。十進数で各桁を右や左にずらせば、10 を掛けたり割ったりしていることになりますね?

ビット列では、左端の 1 ビットは符号ビットであり、 "0 = +", "1 = -" になっています。右にずらして空いたところに符号ビットで埋めるのが >> です。右にずらして、空いたところを 0 で埋めるのが >>> です。

0111 01101101 1010
>>0011 10111110 1101 左端のビットは符号ビット埋め。(2-n)
>>>0011 10110110 1101 符号ビットも 0 埋め。
<<1110 11001011 0100 右端のビットは 0 埋め。(2n)

例えば、n>>s の場合、-8 は 1111 1000 で、一回シフトで -4 が 1111 1100、二回シフトで -2 が 1111 1110 という感じです。正の数の場合は 0 埋めでシフトし、負の数の場合は 1 で埋めてシフトします。負の数の場合に 1 で埋めるのは、負の整数を表現するために 2 の補数表現を使っているからです。整数の正の数、負の数(2 の補数表現)の詳細については、IEEE754 浮動小数点数を参照ください。

a << 3   --> a を 3 ビット左にシフト(2 の 3 乗を掛けたのと同じ)
a >> 4   --> a を 4 ビット右にシフト(2 の 4 乗で割ったのと同じ)
a >>> 5  --> a を 5 ビット右にシフト(符号ビットを無視して 0 で埋めていく)

これらのシフト演算子と共に、次のビット反転演算子も使われます:

 a = 1001 1110
~a = 0110 0001

2進数の詳細は触れませんが、一般に "~a = (-a) - 1" です。

データの保持できるビット数が足りない場合は、保持できる範囲内の下位ビット列だけしか返ってきませんが、演算中は少なくともint型で計算されています。byte型など、int型よりも小さなビットに対して演算すると、結果が予期しない値になってしまいますので、int型かlong型に対してだけ利用しましょう。

ビットシフト演算子には、他にもシフト量の制限(剰余還元)などの規則があるのですが、ここでは説明しません。

インクリメント/デクリメント演算子

インクリメント/デクリメント演算子
式中での評価、更新順序
インクリメントy = y + 1; y++;y を評価してから式を演算し、 y + 1 に更新
++y;y + 1 に更新してから式の評価、演算
デクリメントy = y - 1;y--;評価してから更新
--y;更新してから評価

インクリメント/デクリメント演算子は、二種類づつ存在します。その違いは、式中ではクリティカルなものです。

x = 10;
y = 100;
z = ++y * x;

++y のように前置されると、含まれる式の演算の先に++y が演算されます。即ち、今の場合は z = (y + 1) * x となり、z101 * 101010 となります。

一方、次のように y++ と後置されると、他の全ての計算が終了してからインクリメントやデクリメントが行われます。

z = y++ * x;

この例の場合は後置だから、まず z = y * x が行われてから y++ が実行されます。結果として、z の値は 100 * 101000 となります。

インクリメント/デクリメント演算子は、通常の算術演算子を使うと、次のように表現できます:

インクリメント演算子算術演算子
z = ++y * 10; 1. y = y + 1;
2. z = y * 10;
z = y++ * 10; 1. z = y * 10;
2. y = y + 1;

実際のコーディングでは、どちらかだけ使うようにして、その挙動を覚えるのが一番でしょう。

再帰代入演算子

再帰代入演算子
y = y + 10;y += 10;
y = y - 10;y -= 10;
y = y * 10;y *= 10;
y = y / 10;y /= 10;
y = y % 10;y %= 10;

y = y + 10; のように、右辺に評価される変数自身が表れる、再帰的代入文は、プログラミングでは頻出です。そのための演算子として、ここでは五つ挙げましたが、自身が混乱するようならば使わない方が良いでしょう。

y += 10y = y + 10 だと書きました。単純な場合はそのままなのですが、 ++y += 10 となると話が違います。

++y += 10;++y = ++y + 10;
y = y + 1;
y = y + 10;
y = y + 1;
y = y + 1;
y = y + 10;

代入演算子の場合は、式中で一回だけしか評価されません。それに対して、展開した形のものは現れた回数分だけ評価されます。

他の2項演算子であるビット演算にも、再帰的代入演算子が用意されていますが、ここでは紹介しません。

三項演算子 ?:

今までの演算子は、演算対象が一つか二つでした。例えば、論理値の否定演算子 ! は一項だけに演算します。乗算演算子 * は演算対象が左右に一項ずつ二項あります。 Java では三項を演算対象とする演算子が存在します。ここで紹介する三項演算子は条件演算子とも呼ばれます。

y = a ? x1 : x2

a が真であれば y = x1 であり、偽であれば y = x2 です。

a は真偽値が分かるものでなければなりません。例えば、比較演算式 i < 10 を入れることが出来ます。

a の真偽によって、 y には x1x2 が代入されるわけですから、 x1x2y に代入できるものでなければなりません。

三項演算子は、複数組み合わせる事で複雑怪奇になものが作れるのですが、ここでは説明しません。

サンプル

TestOperator.java:

class TestOperator {
	public static void main(String[] args) {
		int x=10,y=3,z=0;
		System.out.println("算術演算子");
		System.out.println("x=" + x + " y=" + y);
		System.out.println("x+y: " + (x + y));
		System.out.println("x-y: " + (x - y));
		System.out.println("x*y: " + (x * y));
		System.out.println("x/y: " + (x / y));
		System.out.println("x%y: " + (x % y));
	}
}
C:\Java>javac TestOperator.java
C:\Java>java TestOperator
算術演算子
x=10 y=3
x+y: 13
x-y: 7
x*y: 30
x/y: 3
x%y: 1
class TestOperator {
	public static void main(String[] args) {
		int x=10,y=3,z=0;
		System.out.println("再帰的代入文");
		System.out.println("x="+x+" y="+y);
		System.out.print("x+=y:"+(x+=y));
			System.out.println("  x="+x);
		System.out.print("x*=y+10:"+(x*=y+10));
			System.out.println("  x="+x);
			System.out.println("	※x=x*(y+10)と解釈される");
		System.out.print("x%=y:"+(x%=y));
			System.out.println("  x="+x);
		}
}
C:\Java>java TestOperator
再帰的代入文
x=10 y=3
x+=y:13  x=13
x*=y+10:169  x=169
        ※x=x*(y+10)と解釈される
x%=y:1  x=1
class TestOperator {
	public static void main(String[] args) {
		int x=10,y=3,z=0;
		System.out.println("インクリメント/デクリメント演算子");
		//後置インクリメント演算子
		x=10;y=3;
		System.out.println("x="+x+" y="+y);
		z=y++*x;
		System.out.print("1. z=y++*x: ");
		System.out.println("z="+z+" y="+y);
		//前置インクリメント演算子
		x=10;y=3;
		System.out.println("x="+x+" y="+y);
		z=++y*x;
		System.out.print("2. z=++y*x: ");
		System.out.println("z="+z+" y="+y);
	}
}
C:\Java>java TestOperator
インクリメント/デクリメント演算子
x=10 y=3
1. z=y++*x: z=30 y=4
x=10 y=3
2. z=++y*x: z=40 y=4


Copyright © 2001 SUGAI, Manabu. All Rights Reserved.
SEO [PR] [ Windows7 f ^T[o[ Cu`bg SEO