Fantasy_RQG
Fantasy_RQG

Reputation: 155

java Array assignment slowly than List.add()

package test;

import java.util.ArrayList;
import java.util.List;

public class ArrayAndList {
    public static void main(String[] args) {
        int num = 10000000;
        Integer[] mArray = new Integer[num];
        List<Integer> mList = new ArrayList<>(num);

        // array init test
        long iCurr = System.currentTimeMillis();
        for (int i = 0; i < num; i++) {
            mArray[i] = i;
        }
        System.out.println("array init:" + (System.currentTimeMillis() - iCurr));

        // list init test
        iCurr = System.currentTimeMillis();
        for (int i = 0; i < num; i++) {
            mList.add(i);
        }
        System.out.println("list init:" + (System.currentTimeMillis() - iCurr));

        // array get test
        long mExTimeStamp = System.currentTimeMillis();
        long r1 = 0;
        for (int i = 0; i < num; i++) {
            r1 += mArray[i];
        }
        System.out.println("array get:" + (System.currentTimeMillis() - mExTimeStamp));

        // list get test
        mExTimeStamp = System.currentTimeMillis();
        long r2 = 0;
        for (int i = 0; i < num; i++) {
            r2 += mList.get(i);
        }
        System.out.println("list get:" + (System.currentTimeMillis() - mExTimeStamp));
        if (r2 == r1) {
            System.out.println("correct");
        } else {
            System.out.println("error");
        }
    }
}

Result:

array init:3312
list init:3029
array get:19
list get:23
correct

after several times test , List init always faster than Array assignment. Why ? why assignment slower than add()? As I know List composed of Array ? why it can be faster than Array on assignment.

Improved:

package test;

import java.util.ArrayList;
import java.util.List;

public class ArrayAndList {
    public static void main(String[] args) {
        int num = 10000000;
        Integer[] mArray = new Integer[num];
        List<Integer> mList = new ArrayList<>(num);

        // array init test
        long iCurr = System.currentTimeMillis();
        for (int i = 0; i < num; i++) {
            mArray[i] = i;
        }
        System.out.println("array init:" + (System.currentTimeMillis() - iCurr));

        // list init test
        iCurr = System.currentTimeMillis();
        for (int i = 0; i < num; i++) {
            mList.add(i);
        }
        System.out.println("list init:" + (System.currentTimeMillis() - iCurr));

        mList.clear();
        // second init List
        iCurr = System.currentTimeMillis();
        for (int i = 0; i < num; i++) {
            mList.add(i);
        }
        System.out.println("second list init:" + (System.currentTimeMillis() - iCurr));

        // second init Array
        iCurr = System.currentTimeMillis();
        for (int i = 0; i < num; i++) {
            mArray[i] = i;
        }
        System.out.println("second array init:" + (System.currentTimeMillis() - iCurr));

        // array get test
        long mExTimeStamp = System.currentTimeMillis();
        long r1 = 0;
        for (int i = 0; i < num; i++) {
            r1 += mArray[i];
        }
        System.out.println("array get:" + (System.currentTimeMillis() - mExTimeStamp));

        // list get test
        mExTimeStamp = System.currentTimeMillis();
        long r2 = 0;
        for (int i = 0; i < num; i++) {
            r2 += mList.get(i);
        }
        System.out.println("list get:" + (System.currentTimeMillis() - mExTimeStamp));
        if (r2 == r1) {
            System.out.println("correct");
        } else {
            System.out.println("error");
        }
    }
}

Result:

array init:3331
list init:2867
second list init:725
second array init:814
array get:20
list get:25
correct

After improved test Method , test show more interesting and confusing Result. Assignment and add() both be more faster. But assignment is still slower than assignment.

Upvotes: 0

Views: 96

Answers (4)

Fantasy_RQG
Fantasy_RQG

Reputation: 155

Print Gc Info, and When I init the list first , the result is List init slower than Array init. then I print the GC information. The first init will trigger more GC operation, then the first would be slower.

-XX:+PrintGC

int num = 10000000;
Integer[] mArray = new Integer[num];
List<Integer> mList = new ArrayList<>(num);

Result:

[GC (Allocation Failure)  111405K->109709K(143360K), 0.1795657 secs]
[Full GC (Ergonomics)  109709K->109633K(229376K), 0.6283730 secs]
[GC (Allocation Failure)  142913K->143015K(242176K), 0.2237062 secs]
[GC (Allocation Failure)  189095K->189183K(262656K), 0.2695423 secs]
[Full GC (Ergonomics)  189183K->188980K(408576K), 1.6973847 secs]
list init:3084
[GC (Allocation Failure)  255540K->255012K(414208K), 0.2481102 secs]
[GC (Allocation Failure)  327204K->327364K(414208K), 0.3935178 secs]
[Full GC (Ergonomics)  327364K->327061K(625664K), 2.0468699 secs]
array init:2734
[GC (Allocation Failure)  399253K->399381K(742912K), 0.3149823 secs]
[GC (Allocation Failure)  505877K->505989K(748032K), 0.6380493 secs]
second list init:1014
[GC (Allocation Failure)  612485K->612637K(820736K), 0.6460302 secs]
[Full GC (Ergonomics)  612637K->391091K(957952K), 3.1096489 secs]
second array init:3795
array get:19
list get:27
correct

change the type of Array, int type would not new Object. Without GC, Array init is very fast.

-XX:+PrintGC

int num = 10000000;
int[] mArray = new int[num];
List<Integer> mList = new ArrayList<>(num);

Result:

[GC (Allocation Failure)  111405K->109709K(143360K), 0.1745422 secs]
[Full GC (Ergonomics)  109709K->109633K(229376K), 0.6432035 secs]
[GC (Allocation Failure)  142913K->142990K(241664K), 0.2102660 secs]
[GC (Allocation Failure)  188558K->188662K(262656K), 0.2762992 secs]
[Full GC (Ergonomics)  188662K->188469K(408064K), 1.7465284 secs]
list init:3130
array init:12
[GC (Allocation Failure)  255029K->208029K(412672K), 0.1007355 secs]
[GC (Allocation Failure)  279197K->279341K(412672K), 0.3963681 secs]
[Full GC (Ergonomics)  279341K->169276K(461312K), 1.7562075 secs]
second list init:2322
second array init:10
array get:10
list get:26
correct

Change JVM Paramters

-XX:+PrintGC -Xms3g -Xmx3g

int num = 10000000;
Integer[] mArray = new Integer[num];
List<Integer> mList = new ArrayList<>(num);

Result:

list init:131
array init:117
second list init:129
second array init:116
array get:25
list get:24
correct

without the GC operation , List init and Array init is both speed up, And array init is fast than list init. All thing is about the GC operation , thank you everyone

Upvotes: 0

Tagir Valeev
Tagir Valeev

Reputation: 100169

Your benchmark is absolutely wrong: your code is not hot enough. Let's repeat creation and adding 10 times for example:

import java.util.ArrayList;
import java.util.List;

public class ArrayAndList {
    public static Integer[] createArray(int n) {
        Integer[] arr = new Integer[n];
        for (int i = 0; i < n; i++)
            arr[i] = i;
        return arr;
    }

    public static List<Integer> createList(int n) {
        List<Integer> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++)
            list.add(i);
        return list;
    }

    public static void main(String[] args) {
        int num = 10000000;
        for (int i = 0; i < 10; i++) {
            System.gc();
            {
                long start = System.currentTimeMillis();
                List<Integer> l = createList(num);
                long end = System.currentTimeMillis();
                System.out.println("List: " + l.size() + "; time: "
                        + (end - start));
            }
            System.gc();
            {
                long start = System.currentTimeMillis();
                Integer[] a = createArray(num);
                long end = System.currentTimeMillis();
                System.out.println("Arr: " + a.length + "; time: "
                        + (end - start));
            }
        }
    }
}

My box outputs the following numbers:

List: 10000000; time: 7838
Arr: 10000000; time: 6615
List: 10000000; time: 1429
Arr: 10000000; time: 1466
List: 10000000; time: 893
Arr: 10000000; time: 5005
List: 10000000; time: 977
Arr: 10000000; time: 468
List: 10000000; time: 530
Arr: 10000000; time: 218
List: 10000000; time: 228
Arr: 10000000; time: 227
List: 10000000; time: 243
Arr: 10000000; time: 224
List: 10000000; time: 236
Arr: 10000000; time: 226
List: 10000000; time: 232
Arr: 10000000; time: 225
List: 10000000; time: 241
Arr: 10000000; time: 249

So after JIT-compilation, profiling and optimization both tests run equally fast and about 30 times faster than the first iteration which is a random mix of interpreted, C1-compiled and C2-compiled code. Nobody cares about first iteration if your program works long enough. Results are equal, because JIT-compiler is smart and it can remove the unnecessary bounds checking.

In future please use JMH to conduct your benchmark tests.

Upvotes: 2

Piotr Praszmo
Piotr Praszmo

Reputation: 18320

Array assignment or List.add takes almost no time in your case. The thing you are really benchmarking is garbage collector. Scanning through 40 million objects is going to take a lot of time.

Garbage collector is unpredictable. You don't known when full GC will be triggered and when heap size will be increased. Those time differences are the effect of that. If you do more passes, you will see almost no difference between list and array.

Upvotes: 1

Katai
Katai

Reputation: 2937

You should try to split the tests up: Instead of testing every method in the same script, split it up in one script per method each and test it then to ensure that the different parts don't influence each other (generally always write a test script as compact as possible).

I'm not sure if it makes a difference, but it's possible that Java optimizes some things during the mList.clear()

Upvotes: -1

Related Questions