Javaで2つの整数を適切に比較するにはどうすればよいですか?


215

ボックス化されたプリミティブIntegerを次のような定数と比較すると、

Integer a = 4;
if (a < 5)

a 自動的に開梱され、比較が機能します。

ただし、2つのボックス化さIntegersれたものを比較していて、等しいかより小さいかより大きいかを比較したい場合はどうなりますか?

Integer a = 4;
Integer b = 5;

if (a == b)

上記のコードは、それらが同じオブジェクトであるかどうかを確認する結果になりますか、それとも、その場合は自動ボックス化解除されますか?

何について:

Integer a = 4;
Integer b = 5;

if (a < b)


16
さて、あなたが試みたときに何が起こりましたか?何を観察しましたか?
Bart Kiers、2009年

31
@Bart Kiers:明示的な実験は、箱を開けることが起こったことを証明するだけでなく、反証することができました。の==代わりにを使用するとequals正しい結果が得られる場合は、ボックス化された数値がインターンされているか、(おそらくコンパイラの最適化として)再利用されている可能性があります。この質問をする理由は、何が起こっているように見えるのではなく、内部で何が起こっているのかを知るためです。(少なくとも、それが私がここにいる理由です。)
ジムピヴァルスキ2013

あなたのアカウントはどうなりましたか?

回答:


301

いいえ、== Integer、Longなどの間では、参照の等価性をチェックします -つまり

Integer x = ...;
Integer y = ...;

System.out.println(x == y);

これは、等しいオブジェクトではなく同じオブジェクトかどうかをチェックしxy参照します。

そう

Integer x = new Integer(10);
Integer y = new Integer(10);

System.out.println(x == y);

印刷が保証されていますfalse。「小さい」オートボックス化された値のインターンは、トリッキーな結果につながる可能性があります。

Integer x = 10;
Integer y = 10;

System.out.println(x == y);

これはtrue、ボクシングのルール(JLSセクション5.1.7)により印刷されます。それはまだ使用されて参照の等価だが、参照が本当にある等しいです。

ボックス化される値pが-128から127までの整数型リテラル(§3.10.1)、ブールリテラルtrueまたはfalse(§3.10.3)、または '\ u0000'と'\ u007f'を含み(§3.10.4)、aとbをpの2つのボクシング変換の結果とします。a == bの場合は常にそうです。

個人的に私は使用します:

if (x.intValue() == y.intValue())

または

if (x.equals(y))

あなたが言うように、ラッパー型(との間の比較のためにIntegerLongなど)、および数値型(intlongなど)ラッパー型の値がある箱なしとテストが関与プリミティブ値に適用されます。

これは、バイナリ数値昇格の一部として発生します(JLSセクション5.6.2)。個々のオペレーターのドキュメントを見て、それが適用されているかどうかを確認してください。たとえば、==and !=JLS 15.21.1)のドキュメントから:

等値演算子のオペランドが両方とも数値型であるか、一方が数値型で他方が数値型に変換できる場合(§5.1.8)、2進数の数値昇格がオペランドに対して実行されます(§5.6.2)。

そして、のために<<=>および>=JLS 15.20.1

数値比較演算子の各オペランドの型は、プリミティブ数値型に変換可能な型(§5.1.8)である必要があります。そうでないと、コンパイル時エラーが発生します。2進数の数値昇格は、オペランドに対して実行されます(5.6.2)。昇格したオペランドの型がintまたはlongの場合、符号付き整数比較が実行されます。この昇格された型がfloatまたはdoubleの場合、浮動小数点比較が実行されます。

これが、どちらの数値型ではない状況の一部と見なされないことに注意してください。


2
x.compareTo(y) < 0代わりに書きたい理由はありますx < yか?
Max Nanasy、2015

1
@MaxNanasy:すぐに思いつくことではありません。
Jon Skeet、2015

2
Java 1.6.27以降では、Integerクラスのequalsにオーバーロードがあるため、.intValue()を呼び出すのと同じくらい効率的です。値をプリミティブintとして比較します。
otterslide 2016

@otterslideが言ったように、これはJava 8ではもう必要ありません。IntegerとIntegerの比較は、デフォルトでは値によるものです。
Axel Prieto 2018

1
@Axel:オーバーロードを追加しても==演算子の動作は変更されませんが、そうでしょうか?私は今テストする立場にはありませんが、それが変わったとしたら非常に驚きます。
Jon Skeet

44

==オブジェクトの等価性は引き続きテストされます。ただし、だまされるのは簡単です。

Integer a = 10;
Integer b = 10;

System.out.println(a == b); //prints true

Integer c = new Integer(10);
Integer d = new Integer(10);

System.out.println(c == d); //prints false

不等式の例はオブジェクトで定義されていないため機能します。ただし、==比較では、オブジェクトの等価性は引き続きチェックされます。この場合、ボックス化されたプリミティブからオブジェクトを初期化すると、同じオブジェクトが使用されます(aとbの両方に)。プリミティブボックスクラスは不変なので、これは大丈夫な最適化です。


私はそれがテストされているオブジェクトの等価性であると考えました。奇妙な結果がありました。.equals()に置き換えますか?また、私は不平等をそのままにしておくべきだと思いますか、それとも別の方法でやりますか?

オートボクシングの明らかでないエッジケースがいくつかあります。私のIDE(Eclipse)は、ボックス化されていないものを赤で着色するように設定されています。これにより、いくつかの状況でバグから救われました。2つの整数を比較する場合は、.equalsを使用します。不等式を明確にしたい場合は、キャストを明示的に記述します。if((int)c <(int)d)...; 次のことも実行できます:c.compareTo(d)<0 // === c <d
Adam Lewis

12
また、数値リテラルをに変更すると200、両方のテストが印刷されますfalse
Daniel Earwicker、2009年

2
...ほとんどのJVM実装、つまりです。言語仕様によると、結果は実装によって異なる可能性があります。
Daniel Earwicker、2009年

4
これを「参照の等価性」と呼ぶ方が明確だと思います。つまり、あなたが何を意味するのかは明らかです。私は通常、「オブジェクトの等価性」は「equals呼び出された結果」を意味すると理解しています。
Jon Skeet、

28

Java 1.7以降では、Objects.equalsを使用できます。

java.util.Objects.equals(oneInteger, anotherInteger);

引数が互いに等しい場合はtrueを返し、それ以外の場合はfalseを返します。したがって、両方の引数がnullの場合はtrueが返され、1つの引数がnullの場合はfalseが返されます。それ以外の場合、等価性は最初の引数のequalsメソッドを使用して決定されます。


これはnullを処理するので、簡単になります。ありがとう!
Darren Parker

10

== 参照の等価性をチェックしますが、次のようなコードを書くときは

Integer a = 1;
Integer b = 1;

Javaはのための同じ不変を再利用するスマート十分であるab、これは本当です:a == b。好奇心が強い、私はjavaがこの方法で最適化を停止する場所を示す小さな例を書きました:

public class BoxingLol {
    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            Integer a = i;
            Integer b = i;
            if (a != b) {
                System.out.println("Done: " + i);
                System.exit(0);
            }
        }
        System.out.println("Done, all values equal");
    }
}

これを(私のマシンで)コンパイルして実行すると、次のようになります。

Done: 128

1
tl; dr -1はハンドウェービング。stackoverflow.com/questions/15052216/... stackoverflow.com/questions/20897020/... stackoverflow.com/questions/3131136/integers-caching-in-javaなどで、詳細あなたが言及した事柄を説明します。結果の局所性が高いというリスクを伴う疑似テストを作成するよりも、ドキュメント(またはlibソース)を読む方が良い-キャッシュの下限(つまり、デフォルトで-128)を完全に忘れているだけでなく、 1つずれています(最大値は128ではなく127です)

しかし、どのマシンでも同じ結果が得られる保証まったくありません。自分でキャッシュサイズを簡単に増やすことができるため、YMMVです。また、OPの質問は、2つの整数を適切に比較する方法でした - まったく答えていません

ここであなたの意見と認識を尊重します。CSに対する根本的に異なるアプローチがあるだけだと思います。
Cory Kendall

1
それは意見認識に関するものではありません-それはあなたが心から見逃した事実に関するものです。疑似テストを行っても何も証明されず、ハードバッキングデータ(ドキュメント、ソースなど)がなく、OPの質問に答えることができないのは、質の高いQ&AでもCSでもありません。「異なるアプローチ」について-CSは、定義により、科学です。あなたがしたことは科学ではありません。それはだ誤解を招くトリビア(またはそれは次のようになり興味深いコメント適切に述べられている場合、) -あなたはそれがしたい場合は、科学、あなたの答えに根本的な欠陥を修正するか、それらを暴く分別ような、」動作します。

確かに、私は欠陥に対処しようとします。私は下限を忘れなかったし、それが面白いとは思わなかったので、含めないことにしました。私は1つのエラーでオフになっているとは思わない、私は(私の状況では、私のマシンで明らかにした)Javaがこれを最適化するのを止めた方法を述べました、それは128でした。最大値を述べていた場合、この正しい答えは127だっただろうしているよりも、ために
コーリーケンドール

8

tl; dr私の意見では+、値の等価性をチェックするときに、単項を使用してオペランドの1つでボックス化解除をトリガーし、それ以外の場合は単に数学演算子を使用します。理論的根拠は次のとおりです。

==比較Integerは同一性比較であり、これは通常プログラマーが望んでいることではなく、その目的は値の比較を行うことであることはすでに述べました。それでも、コードのコンパクトさ、正確さ、速度の両方の点で、比較を最も効率的に行う方法について少し科学を行いました。

私は通常の方法を使用しました:

public boolean method1() {
    Integer i1 = 7, i2 = 5;
    return i1.equals( i2 );
}

public boolean method2() {
    Integer i1 = 7, i2 = 5;
    return i1.intValue() == i2.intValue();
}

public boolean method3() {
    Integer i1 = 7, i2 = 5;
    return i1.intValue() == i2;
}

public boolean method4() {
    Integer i1 = 7, i2 = 5;
    return i1 == +i2;
}

public boolean method5() { // obviously not what we want..
    Integer i1 = 7, i2 = 5;
    return i1 == i2;
}

コンパイルと逆コンパイル後にこのコードを取得しました:

public boolean method1() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    return var1.equals( var2 );
}

public boolean method2() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method3() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method4() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method5() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2 == var1 ) {
        return true;
    } else {
        return false;
    }
}

簡単にわかるように、メソッド1はInteger.equals()(明らかに)呼び出し、メソッド2〜4はまったく同じコードになり、値を使用して値をアンラップ.intValue()してから直接比較し、メソッド5はID比較をトリガーするだけなので、値を比較します。

(例えばJSによってすでに言及されているように)equals()オーバーヘッドが発生するため(instanceofキャストを実行する必要があり、キャストがチェックされていない)、メソッド2〜4はまったく同じ速度で動作します。キャスト&を最適化する可能性がありinstanceofます。

それは他の比較演算子と非常によく似ています(例:</ >)-それらはアンボクシングをトリガーしcompareTo()ますが、使用しません-しかし、今回intValue()はただのゲッターメソッド(最適化される最有力候補)であるため、操作はHSによって高度に最適化されます。

私の意見では、めったに使われないバージョン4が最も簡潔な方法がある-すべての味付けC / Java開発者は、その単項を知っているプラス、ほとんどのケースであるにキャストに等しいint/ .intValue()-それは少しかもしれないがWTFのいくつかのためのモーメント(主にこれらのdidn有効期間中に単項プラスを使用しないでください)、それは間違いなく意図を最も明確かつ最も簡潔に示します- intオペランドの1つの値が必要であることを示し、他の値も強制的にボックス化解除します。またi1 == i2、プリミティブint値に使用される通常の比較と間違いなく最も類似しています。

私の投票はのために行くi1 == +i2i1 > i2のスタイルIntegerのオブジェクト、両方のパフォーマンス&一貫性の理由から。また、型宣言以外を変更することなく、コードをプリミティブに移植できます。名前付きメソッドを使用することは、私にセマンティックノイズを導入するようなものであり、非常に批判bigInt.add(10).multiply(-3)的なスタイルに似ています。


方法4での+の意味を説明できますか?私はそれをグーグルにしようとしました、しかし私はそのシンボルの通常の使用法(追加、連結)だけを得ました。
Alex Li

1
@AlexLiそれは私が書いたものを正確に意味します- unary +(単項プラス)、例えば、stackoverflow.com

8

呼び出す

if (a == b)

ほとんどの場合動作しますが、常に動作するとは限りませんので、使用しないでください。

2つのIntegerクラスが等しいかどうかを比較する最も適切な方法は、それらが 'a'と 'b'と名付けられていると仮定して、以下を呼び出すことです。

if(a != null && a.equals(b)) {
  System.out.println("They are equal");
}

少し速いこの方法も使用できます。

   if(a != null && b != null && (a.intValue() == b.intValue())) {
      System.out.println("They are equal");
    } 

私のマシンでは、99億回の操作に最初の方法を使用すると47秒、2番目の方法を使用すると46秒かかりました。違いを確認するには、何十億もの値を比較する必要があります。

「a」はオブジェクトであるため、nullになる場合があることに注意してください。この方法で比較しても、nullポインタ例外は発生しません。

大なり小なりを比較するには、

if (a != null && b!=null) {
    int compareValue = a.compareTo(b);
    if (compareValue > 0) {
        System.out.println("a is greater than b");
    } else if (compareValue < 0) {
        System.out.println("b is greater than a");
    } else {
            System.out.println("a and b are equal");
    }
} else {
    System.out.println("a or b is null, cannot compare");
}

1
if (a==b)小さな値に対してのみ機能し、ほとんどの場合機能しません。
トニー

Javaのデフォルトの整数キャッシュであるため、最大127まで機能します。これにより、127までのすべての数値が同じ参照値を持つようになります。必要に応じて、キャッシュを127より大きく設定することもできますが、安全のために==を使用しないでください。
otterslide 2017

2

2つの整数の比較には、常にequals()メソッドを使用する必要があります。これが推奨される方法です。

==を使用して2つの整数を比較すると、JVMの内部最適化により、特定の範囲の整数値(-128から127の整数)で機能します。

例をご覧ください:

ケース1:

整数a = 100; 整数b = 100;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}

上記の場合、JVMはキャッシュされたプールからaとbの値を使用し、整数オブジェクトの同じオブジェクトインスタンス(したがってメモリアドレス)を返し、両方が等しくなるようにします。特定の範囲値に対して最適化JVMが行います。

ケース2:この場合、aとbは-128から127の範囲ではないため、等しくありません。

整数a = 220; 整数b = 220;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}

適切な方法:

Integer a = 200;             
Integer b = 200;  
System.out.println("a == b? " + a.equals(b)); // true

これがお役に立てば幸いです。


1

私の場合、2つIntegerのs を比較して、両者が等しいかどうかを確認しましたnull。同様のトピックを検索しましたが、これについてエレガントなものは見つかりませんでした。シンプルなユーティリティ関数を思いつきました。

public static boolean integersEqual(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return true;
    }
    if (i1 == null && i2 != null) {
        return false;
    }
    if (i1 != null && i2 == null) {
        return false;
    }
    return i1.intValue() == i2.intValue();
}

//considering null is less than not-null
public static int integersCompare(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return 0;
    }
    if (i1 == null && i2 != null) {
        return -1;
    }
    return i1.compareTo(i2);
}

-1

比較メソッドは、int型(x == y)またはInteger型(x.equals(y))と正しい演算子に基づいて実行する必要があるため

public class Example {

    public static void main(String[] args) {
     int[] arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<arr.length-1; j++)
            if((arr[j-1]!=arr[j]) && (arr[j]!=arr[j+1])) 
                System.out.println("int>"+arr[j]);


    Integer[] I_arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<I_arr.length-1; j++)
            if((!I_arr[j-1].equals(I_arr[j])) && (!I_arr[j].equals(I_arr[j+1]))) 
                System.out.println("Interger>"+I_arr[j]);
    }
}

-2

このメソッドは2つのIntegerとnullチェックを比較します。テストを参照してください

public static boolean compare(Integer int1, Integer int2) {
    if(int1!=null) {
        return int1.equals(int2);
    } else {
        return int2==null;
    }
    //inline version:
    //return (int1!=null) ? int1.equals(int2) : int2==null;
}

//results:
System.out.println(compare(1,1));           //true
System.out.println(compare(0,1));           //false
System.out.println(compare(1,0));           //false
System.out.println(compare(null,0));        //false
System.out.println(compare(0,null));        //false
System.out.println(compare(null,null));     //true

4
このため、自分Objects.equals(x,y)でロールするのではなく、メソッドを使用する方が良いと思います。
ryvantage 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.