整数は不変


100

これはおそらく非常に愚かであることは知っていますが、多くの場合、JavaのIntegerクラスは不変であると主張していますが、次のコードは

Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);

問題なく実行され、(期待される)結果が得られます。つまり、aの値が実質的に変更されています。それは、整数が変更可能であることを意味しませんか?二次的な質問とちょっとした話題:「不変のクラスはコピーコンストラクタを必要としません」。誰もが理由を説明してくれませんか?


12
クラスは不変ですが、オートボクシングを作っているファンキーなことが起こる:stackoverflow.com/questions/3085332/...を
WKL

おかげで、ボクシングはグーグルに必要なキーワードだった:)
K.Steff '06

7
不変を最終値または定数値と混同しています。
熱狂的なコード

回答:


94

不変とは、それaが別の値と等しくなることは決してないという意味ではありません。たとえば、Stringも不変ですが、私はまだこれを行うことができます:

String str = "hello";
// str equals "hello"
str = str + "world";
// now str equals "helloworld"

str変更されていませんが、strまったく同じように、完全に新しくインスタンス化されたオブジェクトになりましたInteger。したがって、の値はa変化しませんでしたが、完全に新しいオブジェクト、つまりに置き換えられましたnew Integer(6)


14
「これはstrが完全に新しくインスタンス化されたオブジェクトだからです」。または、むしろ、str(変数)が新しいオブジェクトを指します。オブジェクト自体は変更可能ではありませんが、変数は最終的なものではないため、別のオブジェクトを指す可能性があります。
サンドマン、

はい、+=操作の結果としてインスタンス化された別のオブジェクトを指しています。
Travis Webb

11
厳密に言えば、それはする必要はないことが新しいオブジェクト。ボクシングは使用しInteger.valueOf(int)、そのメソッドはIntegerオブジェクトのキャッシュを維持します。結果だから、+=上のInteger変数が以前に存在したことをオブジェクト可能性があり(あるいは、それも同じオブジェクトである可能性が...の場合a += 0)。
スティーブンC

1
なぜJavaDoc for Stringは明示的に不変と言っていますが、JavaDoc for Integerはそうではないのですか?その違いが私がこの質問を読んでいる理由です...
cellepo

50

aいくつかのInteger(3)への「参照」であり、あなたの速記はa+=b実際にこれを行うことを意味します:

a = new Integer(3 + 3)

したがって、いいえ、整数は変更可能ではありませんが、それらを指す変数は*です。

*不変の変数を持つことが可能です。これらはキーワードfinalで示されます。これは、参照が変更されないことを意味します。

final Integer a = 3;
final Integer b = 3;
a += b; // compile error, the variable `a` is immutable, too.

18

を使用してオブジェクトが変更されたと判断できますSystem.identityHashCode()(より適切な方法はプレーンを使用する==ことですが、値ではなく参照が変更されたことはそれほど明白ではありません)。

Integer a = 3;
System.out.println("before a +=3; a="+a+" id="+Integer.toHexString(System.identityHashCode(a)));
a += 3;
System.out.println("after a +=3; a="+a+" id="+Integer.toHexString(System.identityHashCode(a)));

プリント

before a +=3; a=3 id=70f9f9d8
after a +=3; a=6 id=2b820dda

オブジェクトが参照している基になる "id" aが変更されていることがわかります。


System.identityHashCode()は非常に優れたヒントです。これをありがとう。
Ad Infinitum 2017年

10

最初に尋ねられた質問に、

Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);

整数は不変なので、上記で起こったことは 'a'が値6の新しい参照に変更されました。初期値3はメモリに参照なしで残っているため(変更されていません)、ガベージコレクションできます。

これが文字列に発生すると、参照があると予想されるため、整数よりも長い期間(PermGenスペースで)プールに保持されます。


8

はい整数は不変です。

Aはオブジェクトを指す参照です。+ = 3を実行すると、Aが別の値で新しいIntegerオブジェクトを参照するように再割り当てされます。

元のオブジェクトを変更したことはなく、参照を別のオブジェクトにポイントしました。

オブジェクトと参照の違いについては、こちらをお読みください


他のすべての複雑な説明がそこにあるので、簡単かつ簡単に素人の言葉で言った:)
Roshan Fernando

5

不変とは、変数の値を変更できないという意味ではありません。これは、新しい割り当てが新しいオブジェクトを作成し(新しいメモリ位置を割り当てる)、値がそれに割り当てられることを意味します。

これを理解するために、整数の代入をループで実行し(整数はループの外側で宣言されています)、メモリ内のライブオブジェクトを調べます。

不変オブジェクトにコピーコンストラクターが必要ない理由は、単純な常識です。割り当てごとに新しいオブジェクトが作成されるため、言語は技術的には既にコピーを作成しているため、別のコピーを作成する必要はありません。


2

「不変クラスはコピーコンストラクタを必要としません」。誰もが理由を説明したいと思いますか?

その理由は、不変クラスのインスタンスをコピーする必要はほとんどない(またはコピーの任意の時点でさえ)ためです。オブジェクトのコピーは元のオブジェクトと「同じ」である必要があり、同じである場合は作成する必要はありません。

ただし、いくつかの基本的な前提があります。

  • これは、アプリケーションがクラスのインスタンスのオブジェクトIDに意味を持たないことを前提としています。

  • これは、クラスがオーバーロードされてequalsおりhashCode、インスタンスのコピーが元の「と同じ」であると想定しています...これらのメソッドによると...

これらの仮定のいずれかまたは両方が誤っている可能性があり、コピーコンストラクターの追加が必要になる場合あります。


1

これは私が不変を理解する方法です

int a=3;    
int b=a;
b=b+5;
System.out.println(a); //this returns 3
System.out.println(b); //this returns 8

intが変化する可能性がある場合、 "a"は8を出力しますが、それは不変なので、そうではありません。それが3である理由です。あなたの例は新しい割り当てです。


0

Integer(およびFloat、Shortなどのその他の信条)は単純なサンプルコードで不変であることを明確にできます。

サンプルコード

public class Test{
    public static void main(String... args){
        Integer i = 100;
        StringBuilder sb = new StringBuilder("Hi");
        Test c = new Test();
        c.doInteger(i);
        c.doStringBuilder(sb);
        System.out.println(sb.append(i)); //Expected result if Integer is mutable is Hi there 1000
    }

    private void doInteger(Integer i){
        i=1000;
    }

    private void doStringBuilder(StringBuilder sb){
        sb.append(" there");
    }

}

実結果

結果は期待される結果ではなくHi There 100になります(sbとiの両方が変更可能なオブジェクトの場合)Hi There 1000

これは、メインでiによって作成されたオブジェクトが変更されていないのに対し、sbは変更されていることを示しています。

そのため、StringBuilderは変更可能な動作を示しましたが、Integerは示しませんでした。

したがって、整数は不変です。 したがって証明された

整数だけのない別のコード:

public class Test{
    public static void main(String... args){
        Integer i = 100;
        Test c = new Test();
        c.doInteger(i);
        System.out.println(i); //Expected result is 1000 in case Integer is mutable
    }

    private void doInteger(Integer i){
        i=1000;
    }


}

あなたは2つの異なることをしています-整数を再割り当てしようとすることと、文字列ビルダーでメソッドを呼び出すことです。そうした場合private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }sb変更はありません。
MT0 2016

StringBuilder(変更可能)を追加して、変更可能な別のオブジェクトとIntegerを並置するだけです。あなたがしたい場合は、すべてのStringBuilder関連するコードを削除して、ちょうど私が100見るためにプリントアウトすることができます
アッシュートッシュニガム

これは不変性を証明していません-あなたがやっているすべては再ハッシングである。この例のJavaが使用することを実証することにより、価値の渡し(およびオブジェクトのために渡された値がポインタであることを)。
MT0 2016

試してみるprivate void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
MT0

@ MT0値渡しする場合、StringBuilderは同じオブジェクトを指していますが、Integerは同じオブジェクトへの参照ではなく新しいコピーを渡します。doIntegerで印刷すると、main関数ではなく、関数が所有するコピーが表示されます。メインでiが指すオブジェクトが同じかどうかを確認します。概念が明確になれば幸いです:)また、StringBuilderの不変バージョンはStringです。サンプルを共有してほしい場合はお知らせください。
Ashutosh Nigam

-1
public static void main(String[] args) {
    // TODO Auto-generated method stub

    String s1="Hi";
    String s2=s1;

    s1="Bye";

    System.out.println(s2); //Hi  (if String was mutable output would be: Bye)
    System.out.println(s1); //Bye

    Integer i=1000;
    Integer i2=i;

    i=5000;

    System.out.println(i2); // 1000
    System.out.println(i); // 5000

    int j=1000;
    int j2=j;

    j=5000;

    System.out.println(j2); // 1000
    System.out.println(j); //  5000


    char c='a';
    char b=c;

    c='d';

    System.out.println(c); // d
    System.out.println(b); // a
}

出力は:

こんにちはバイ1000 5000 1000 5000 d a

つまり、charはmutableであり、String Integerとintは不変です。


1
この回答は他の人からの情報を提供していません。
Giulio Caccin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.