#演算子の種類
- あらかじめ決められた処理を行うための記号
- 演算子によって処理される変数を被演算子(オペランド)
- 算術演算子
- 代入演算子
- 関係演算子
- 論理演算子
- ビット演算子
#算術演算子
+ 演算子
-
- はオペラントの型によって動作が変化
- オペランドのいづれかが文字列の場合、+は文字列として結合
- 片方がオブジェクト以外の場合、toStringメソッドで文字列に変換して結合
import java.time.LocalDateTime;
public class Main {
public static void main(String[] args) throws Exception {
System.out.println("a"+LocalDateTime.now()); //a2020-10-29T…
}
}
- 数値と非オブジェクトの+演算は不可能
-
- で文字列を結合すると
- 元の文字列、連結する文字列、結果文字列の3個のString文字列を生成しているのでガベージコレクションの増大に。。
- →StringBuilderクラスのappendメソッドを使うと、一定のサイズを確保した可変長の文字列を表すので、確保した領域で文字列を自由に変更できる
- →インスタンスの生成・破棄が少ない!
- あらかじめ文字量が想定できるなら、インスタンス化の際にサイズを明示して文字列拡張してメモリ再割り当てする処理を効率化
- 最新のJavaでは+ 演算子は内部的にStringBuilderに変換されるので単発の文字列連結の場合+演算子でOK
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
var builder = new StringBuilder();
for (var i=0; i<100000; i++){
builder.append("mohu");
//result += "mohu" ;
}
var result = builder.toString();
System.out.println(result);
}
}
###時間比較
- stringBuilderが7msで圧勝!
import java.util.Date;
public class Main {
public static void main(String[] args) { new Main().run(); }
private void run(){
long startTime = getNow();
String hoge = "";
for (int i= 0; i < 100000; i++){
hoge = hoge + "mohu";
}
long elapse = getNow() - startTime;
System.out.println("elapse "+ elapse ); //2716ms
}
//Dateで今の時間をとる
private long getNow(){
return new Date().getTime();
}
}
import java.util.Date;
public class Main {
public static void main(String[] args) { new Main().run(); }
private void run(){
long startTime = getNow();
StringBuilder stringBuilder = new StringBuilder();
for (int i= 0; i < 100000; i++){
stringBuilder.append("mohu");
}
long elapse = getNow() - startTime;
System.out.println("elapse "+ elapse ); //7ms
}
//Dateで今の時間をとる
private long getNow(){
return new Date().getTime();
}
}
インクリメント演算子
- ++i:前置加算(代入前に加算)
-
i = 3; j = ++i
→ i = 4 j = 4
-
- i++:後置加算(代入後に加算)
-
i = 3; j = i++
→ i = 4 j = 3
-
除算では型に注意
- / :除算
6 / 3 = 2
- % : 剰余 (あまり)
10 % 4 = 2
//NG **整数同士の計算なので結果も整数**
System.out.println( 3 / 4 ); //0
//NG **整数化されるのは演算のタイミング!**
double result = 3 / 4 ;
System.out.println(result); //0.0
//OK **オペラントのいづれかを明示的にdoubleにする
System.out.println( 3d / 4 ); //0.75
##ゼロの取り扱い
//NG **定数0の徐算はエラー**
System.out.println( 5 / 0 ); //0
//OK **オペラントが浮動小数点**
System.out.println( 5d / 0 ); //Infinity(無限大)
//OK
System.out.println( 5d % 0 ); //NaN (Not a number: 非数)
##浮動小数点の演算での注意
System.out.println(Math.floor(( 0.7 + 0.1 )*10 )); //7.0
- 浮動小数点型は二進数で演算
- 10進数の0.1→2進数では無限循環小数になる
- →BigDecimalクラスを使う
- 内部的には小数点すうを整数と少数程運位置を分けて管理
- (注)インスタンス化するときに浮動小数点数リテラルではなく文字列を使う!
var bd1 = new BigDecimal(0.7); //NG例
import java.util.*;
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) throws Exception {
var bd1 = new BigDecimal("0.7");
var bd2 = new BigDecimal("0.1");
var bd3 = new BigDecimal("10");
System.out.println(bd1.add(bd2).multiply(bd3)); //8.0
}
}
#代入演算子
- 基本型・参照型で挙動が異なる
- 基本型の値は変数にそのまま格納されるので元の変数を変更してもコピーした変数(y)は影響しない
- 参照型では値を格納しているメモリ上のアドレスが変数に格納しているので、コピーしたものも、アドレスが同じオブジェクトを指す
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
var x = 1;
var y = x;
x += 10;
System.out.println(x); //11
System.out.println(y); //1
var builder1 = new StringBuilder("abc");
var builder2 = builder1; //アドレスをコピー
builder1.append("de");
System.out.println(builder1.toString()); //abcde
System.out.println(builder2.toString()); //abcde
}
}
##定数への代入
- 基本型では変更できない
- 参照型では定数そのものを書き換えることはできないが、帯イジェクトはそのままで内容のみ書き替えることができる
- String型は不変型なので値を変更できない
//NG例
final int VALUE = 10;
VALUE = 15;
final int[] VALUES = { 10, 20, 30 } ;
//NG **定数そのものを再代入は不可
VALUES = new int[] { 10, 20, 30 } ;
//OK
VALUES[0] = 100;
System.out.println(Arrays.toString(VALUES)); //[100, 20, 30]
##関係演算子(比較演算子)
- 両辺を比較、true/falseを返す
- 同一性(Identity)
- 同値性(Equivalence)
== と equals の違い
- == 演算子は**オペラントの同一性(=値を格納している参照先)**を比較、異なるオブジェクトの場合false
- 値そのものを格納している基本型では、値が比較の対象になるので問題ない
- equalsメソッドはオブジェクトの値を比較
var builder1 = new StringBuilder("neko");
var builder2 = new StringBuilder("neko");
System.out.println(builder1 == builder2); //false
var str1 = "neko";
var str2 = "neko";
System.out.println(str1 == str2); //true
System.out.println(str1 == "ne" + "ko"); //true 文字列(Stringオブジェクト)の場合は同値であると同一とみなす
###浮動小数の比較
- BigDeciamal
- 判定結果が0ならオブジェクトの数値と引数が等しい、1は引数の方が大きい、-1は引数の方が小さい
- (注)BigDeciamalでのequalsメソッドは有効数字まで判定
- 1.0 ≠ 1.00
- →compareToメソッドを使う
- 丸め単位を使う
- EPSILON:誤差許容範囲
- 差がEPSILON範囲内ならtrue
System.out.println( 0.2*3 == 0.6 ); //false
//BigDecimal
import java.util.*;
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) throws Exception {
var bd1 = new BigDecimal("0.2");
var bd2 = new BigDecimal("4");
var bd3 = new BigDecimal("0.8");
System.out.println(bd1.multiply(bd2).compareTo(bd3)); //0
}
}
//丸め単位を使う
public class Main {
public static void main(String[] args) throws Exception {
final double EPSILON = 0.00001; //小数第5位まで保証
var x = 0.2 * 4;
var y = 0.8;
System.out.println(Math.abs(x - y) < EPSILON); //true
}
}
###配列の比較
- Arrayクラスのequalsメソッドを使う
- 入れ子配列はdeepEqualsメソッド
- Compareメソッド(Java9以降)ははい入れつを先頭要素から順に比較、大きい/小さい要素があったら全体の大小を確定
public class Main {
public static void main(String[] args) throws Exception {
var data1 = new String[] { "a", "b", "c" };
var data2 = new String[] { "a", "b", "c" };
var data3 = new int[][] {
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 },
};
var data4 = new int[][] {
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 },
};
System.out.println(Arrays.equals(data1, data2)); //true
System.out.println(Arrays.deepEquals(data3, data4)); //true
}
}
//Compareメソッド
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
var data1 = new int[] {1, 3};
var data2 = new int[] {1, 2, 3};
var data3 = new int[] {1, 2, 3};
var data4 = new int[] {1, 3, 1};
var data5 = new int[] {0, 0, 6};
var data6 = new int[] {6, 0, 0};
System.out.println(Arrays.compare(data1, data2)); //1
System.out.println(Arrays.compare(data3, data4)); //-1
System.out.println(Arrays.compare(data2, data3)); //0
System.out.println(Arrays.compare(data3, data5)); //1
System.out.println(Arrays.compare(data3, data6)); //-1
}
}
##条件演算子(三項演算子)
- (条件式) ? 式A : 式B
- AとBを比較するとき(式A:式B) 式A、Bは何らかの値を返す必要がある
public class Main {
public static void main(String[] args) throws Exception {
var age = 20;
System.out.println(age >= 20 ? "大人" : "子供"); //大人
}
}
##論理演算子
- 複数の条件式を論理的に結合し、より複雑な条件式を表現できる
- &&:論理積(どちらもtrueの時true)
- ||:論理和(片方trueのときtrue)
- ^:排他的論理和(片方のみtrueの時true)
- !:否定(式がtrueの時false,式がfalseの時true)
###ショートカット演算(短絡演算)
-
左式のみ評価されて右が評価されない演算
- 論理積では左辺がfalseのとき右辺にかかわらずfalse
- 論理和では左辺がtrueの時右辺にかかわらずtrue
- &や|演算子はショートカットしない
- nullチェックにつかわれる
- nullであればstartsWithメソッドは呼ばれない
- &&を&にするとNullPoiintExceptionエラーになる
//nullチェック
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
String str = "http://hoge";
if (str != null && str.startsWith("http://")) {
// if(str.startsWith("http://")) {
System.out.println("「http://~」で始まります。"); //「http://~」で始まります。
}
}
}
//以下のメソッドでもnullチェック可能
if(!Objects.isNull(str)&&str.startsWith("http://"))
{...}
if(Objects.nonNull(str)&&str.startsWith("http://"))
{...}
##ビット演算子
###ビット論理演算子
- 整数を2進数に変換した上で各桁について論理演算
- 論理積は両方1の時結果も1(true)
- 演算の結果を再び10進数に戻したものを返す
- 否定演算はビットを反転させる
- 0101の否定→(反転)1010→(結果)−6 //結果は10ではない!
- 正負を表す符号も反転させる!
- ビットで負数は”ビット反転後に1を加えたものが絶対値”
- 0101に1を加えた0110(2進数)→符号も加味して-6(10進数)という結果になる
###ビットシフト演算子
- 10進数を2進数として演算し、桁を左右に指定文移動する
- 1010(2進数)→左2シフト→101000→結果40(10進数)
- 算術シフト(>>):最上位のビット(符号)を維持するシフト
- 論理シフト(>>>):最上位のビットに寄らず0埋めする
##演算子の優先順位
- 明示的に()で優先度を記述しよう
- 代表例
【優先度高い順】 |
---|
引数 , [] , . |
++(後置), --(後置) |
++(前置), +(単項), ! , ~ |
* , / , % |
+ , - |
> , >= |
== , != |
& |
^ |
&& |
?: |
= , += , &= |
##結合則
- 同じ優先度の場合、左結合(左→右)と右結合(右→左)がある
- 右結合は代入演算子のみ
- j = i +=10
- j = ( i +=10 )
- =と+=は同じ優先度で右結合なので右から評価
- 10を加えた結果がjに代入される