【Javaベース】ArrayListとメモリ理解、double精度

18390 ワード

最近コードを測っている間に不思議なことが起こりました.前に勉強した知識の抜け穴のはずです.ここに記録してください.

に質問


簡略化コードのテスト:
public class TestDemo {
    public static void main(String[] args) {
        Event e = new Event("0.001","10","20","1");	//(timestamp,x,y,polarity)
        ArrayList<Event> events = new ArrayList<>();
        events.add(e);
        events.add(e);
        events.add(e);

        for(Event event : events){
            event.timestamp+=1.0;
            System.out.println(event.toString());
        }

        System.out.println(events.toString());
    }
}

簡単に言えば、Eventクラスのオブジェクトを作成し、要素がEventクラスのオブジェクトであるArrayListオブジェクトを作成します.複数の異なるオブジェクトを作成するのがおっくうなので、この3つの要素は同じであり、forループで要素のメンバー変数timestampを1.0ずつインクリメントし、コンソールから出力することを目的としています.印刷されたのはこんなものだと思っていました.
Event{x=10, y=20, timestamp=1.001, polarity=1, label='false'}
Event{x=10, y=20, timestamp=1.001, polarity=1, label='false'}
Event{x=10, y=20, timestamp=1.001, polarity=1, label='false'}

しかし、実際の実行結果は次のとおりです.
Event{x=10, y=20, timestamp=1.001, polarity=1, label='false'}
Event{x=10, y=20, timestamp=2.001, polarity=1, label='false'}
Event{x=10, y=20, timestamp=3.001, polarity=1, label='false'}

[Event{x=10, y=20, timestamp=3.001, polarity=1, label='false'}, 
Event{x=10, y=20, timestamp=3.001, polarity=1, label='false'},
 Event{x=10, y=20, timestamp=3.001, polarity=1, label='false'}]


つまり、各要素の値が異なり、最後にforループから出たArrayListから印刷された要素の値がforループの最後の要素である.またArrayListの長さが違う時に試してみました.例えば、私がメイン関数eventにいるとき、上記の例です.add(e)この文が4つある場合、このような結果が得られます.
Event{x=10, y=20, timestamp=1.001, polarity=1, label='false'}
Event{x=10, y=20, timestamp=2.001, polarity=1, label='false'}
Event{x=10, y=20, timestamp=3.001, polarity=1, label='false'}
Event{x=10, y=20, timestamp=4.0009999999999994, polarity=1, label='false'}

[Event{x=10, y=20, timestamp=4.0009999999999994, polarity=1, label='false'},
 Event{x=10, y=20, timestamp=4.0009999999999994, polarity=1, label='false'}, 
 Event{x=10, y=20, timestamp=4.0009999999999994, polarity=1, label='false'}, 
 Event{x=10, y=20, timestamp=4.0009999999999994, polarity=1, label='false'}]


ここでは前回の問題以外にも精度の問題が発生しています.私のEventクラスのtimestampデータ型はdoubleです.

考える


ここで最初の問題について、私が最初に考えたのは深いコピー、浅いコピーの概念です.私の理解の中で深いコピーはアドレスで、数値は同時にコピーして、heapで新しいメモリ空間を開くことに相当します.浅いコピーはアドレスのみをコピーし、数値をコピーしない一時変数であり、stackで作成されます.ここでの出力表象はArrayListの要素のように毎回要素eを追加しているが,実際にaddが追加しているのはeのアドレスである.メモリ領域には常にeオブジェクトが1つしかありません.したがって、forループでは、オブジェクト内のメンバー変数が操作されるたびに、元のオブジェクトが操作され、その結果、ArrayList内の各オブジェクトのメンバー変数が変更される.ここまで来ると、これは深浅コピーの内容ではないかもしれないことに気づきました.私の理解の問題です.
Event e = new Event(...)

この動作はheapにメモリ空間を開き,変数eにアドレスを割り当てることである.ArrayListでのadd操作では,アドレスeを要素としてどんどん加えている.したがって、その結果、要素内のオブジェクトが変更されたように見えますが、毎回、オブジェクト自体ではなくアドレスが指すオブジェクトが変更されます.だから、newがあってこそ新しいオブジェクト(ここではコピーの作成方法はともかく)があり、主に自分に住所とオブジェクトの違いを注意したいと思っています.

テスト


debugは自分の推測を証明します:ここで私はe.hashCode()を呼び出して、アドレス値が変換されたハッシュ値を返して、本当のメモリアドレスを代表しませんが、同じ説明のアドレス値は同じです.
1826771953
1826771953
1826771953

そこで別の方法で推測を証明しました.何度もnewの内容が同じです.
public class TestDemo {
    public static void main(String[] args) {
        Event e1 = new Event("0.001","10","20","1");
        Event e2 = new Event("0.001","10","20","1");
        Event e3 = new Event("0.001","10","20","1");
        ArrayList<Event> events = new ArrayList<>();
        events.add(e1);
        events.add(e2);
        events.add(e3);
        for(Event event : events){
            event.timestamp+=1.0;
            System.out.println(event.toString());
            System.out.println(event.hashCode());
        }
    }
}

印刷値が予想通りであり、アドレスが異なる.
Event{x=10, y=20, timestamp=1.001, polarity=1, label='false'}
1406718218
Event{x=10, y=20, timestamp=1.001, polarity=1, label='false'}
245257410
Event{x=10, y=20, timestamp=1.001, polarity=1, label='false'}
1705736037

Doubleタイプの精度の問題


参照先:https://blog.csdn.net/u011032983/article/details/51810504
ぐるっと調べてみるとJavaのdoubleタイプは面倒そうでした.学校に通っているときにarchitectureを勉強していると思うと、浮動小数点数が0,1で表されているときは無限に循環しない、つまり十進数進数進数進数からバイナリへの変換という過程自体に精度の欠如があり、変換の過程には四捨五入がある.ポイントはあなたの計算にどれだけの精度が必要かです.精度が高い場合はdouble変数は推奨されません.本例では,出力がきれいであるために,出力時に制御し,出力時に四捨五入することができる.