简体   繁体   English

数组应该比 ArrayLists 快这么多吗?

[英]Are arrays supposed to be faster than ArrayLists by this much?

I have implemented two methods, shuffleList and shuffleArray which use the exact same functionality.我已经实现了两种方法, shuffleListshuffleArray ,它们使用完全相同的功能。 And I have an ArrayList of half millions integer, and an array of the same half million ints.我有一个半百万个整数的 ArrayList 和一个半百万个整数的数组。 In my benchmarking code, which performs each one of the methods a 100 times on the corresponding array or ArrayList and records the time, it looks like shuffleArray takes around 0.5 seconds while shuffleList takes around 3.5 seconds, even though the code does not use any ArrayList methods but get and set, which are supposed to work as fast as they work in an array.在我的基准代码中,它在相应的数组或 ArrayList 上执行每个方法 100 次并记录时间,看起来shuffleArray大约需要 0.5 秒而shuffleList大约需要 3.5 秒,即使代码不使用任何 ArrayList方法但 get 和 set ,它们应该像在数组中一样快速地工作。

Now I know that ArrayLists are a little bit slower because they internally use arrays but with some additional code, but does it make this big of a difference?现在我知道 ArrayLists 有点慢,因为它们在内部使用数组但有一些额外的代码,但是它有这么大的区别吗?

     void shuffleList(List<Integer> list){
        Random rnd = ThreadLocalRandom.current();
        for(int i=list.size()-1;i>0;i--){
            int index=rnd.nextInt(i+1);
            int a=list.get(index);
            list.set(index,list.get(i));
            list.set(i,a);
        }
    }

    void shuffleArray(int[] ar)
    {
        Random rnd = ThreadLocalRandom.current();
        for (int i = ar.length - 1; i > 0; i--)
        {
            int index = rnd.nextInt(i + 1);
            int a = ar[index];
            ar[index] = ar[i];
            ar[i] = a;
        }
    }

Benchmarking code:基准代码:

import org.openjdk.jmh.Main;
import org.openjdk.jmh.annotations.*;

@BenchmarkMode(Mode.AverageTime)
public class MyBenchmark {


    @Benchmark
    @Fork(value = 1)
    @Warmup(iterations = 3)
    @Measurement(iterations = 10)
    public void compete() {
        try {
            Sorting sorting = new Sorting();
            sorting.load();
            System.out.println(sorting.test());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        Main.main(args);
    }
}


    protected List<Integer> list = new ArrayList<Integer>();
    protected List<int[]> arrays= new ArrayList<>();

    protected void load(){
        try (Stream<String> stream = Files.lines(Paths.get("numbers.txt"))) {
            stream.forEach(x -> list.add(Integer.parseInt(x)));
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally{
            int[] arr =new int[list.size()];
            for(int i=0;i<list.size();i++)
                arr[i]=list.get(i);
            arrays.add(arr);
        }
    }

    protected double test(){
        int arr[]=arrays.get(0);
        Stopwatch watch = new Stopwatch();
        for (int i=0; i<100; i++){
          shuffleArray(arr);
          shuffleList(list);
        }
        return watch.elapsedTime();
    }

I comment out one of the methods on the for loop and use the other.我注释掉 for 循环中的一种方法并使用另一种方法。

Update:更新:

I did what a lot of you suggested of changing Int a to Integer a in the shuffleList method, and it is making it a little bit faster, it is 3 seconds instead of 3.5 now, but I still think it is a big difference.我做了你们很多人建议的将shuffleList方法中的Int a更改为Integer ashuffleList ,这让它更快了一点,现在是 3 秒而不是 3.5 秒,但我仍然认为这是一个很大的不同。

It is worth mentioning that changing int[] arr to Integer[] arr in the shuffleArray method with keeping int a as it is to simulate the boxing and unboxing time for the array does actually make it a lot slower, it makes it take around 3 seconds, so I can make the array as slow as the ArrayList but I can not do the opposite.值得一提的是,在shuffleArray方法shuffleArray int[] arr 更改为 Integer[] arr 并保留 int a 以模拟数组的装箱和拆箱时间实际上确实使它变慢了很多,它需要大约 3秒,所以我可以让数组和 ArrayList 一样慢,但我不能做相反的事情。

Update:更新:

Using Collections.swap() in shuffleList did indeed make it as fast as the array, but I still do not understand why, is my benchmarking too sensetive or does it really matter?shuffleList使用 Collections.swap() 确实使它和数组一样快,但我仍然不明白为什么,是我的基准测试太有意义还是真的很重要?

Final shuffleList code, courtesy of Andy Turner and Joop Eggen:最终shuffleList代码,由 Andy Turner 和 Joop Eggen 提供:

    protected void shuffleList(List<Integer> list){
        Random rnd = ThreadLocalRandom.current();
        for(int i=list.size()-1;i>0;i--){
            int index=rnd.nextInt(i+1);
            Collections.swap(list, i, index);
        }
    }

Use Integer a , which save one unboxing and one boxing operation.使用Integer a ,可以节省一次拆箱和一次装箱操作。

    for (int i = list.size()-1; i>0; i--){
        int index=rnd.nextInt(i+1);
        Integer a=list.get(index);
        list.set(index,list.get(i));
        list.set(i,a);
    }

And the Integer objects use more memory. Integer 对象使用更多内存。


@Andy Turner mentioned the exist Collections#swap. @Andy Turner 提到了现有的 Collections#swap。

    for (int i = list.size()-1; i > 0; i--) {
        int index = rnd.nextInt(i+1);
        Collections.swap(list, i, index);
    }

Without warm-up of JIT compiler this might slow-down the bench-mark, but will look better in production code.如果不预热 JIT 编译器,这可能会减慢基准测试的速度,但在生产代码中看起来会更好。 Though then you would probably use the Collections.shuffle anyway.虽然那样你可能会使用Collections.shuffle反正。


As commented the swap version is fast too.正如评论的那样,交换版本也很快。 First the OP showed using the right microbenchmarking code.首先,OP 显示使用正确的微基准测试代码。

swap uses the original Integer class too. swap 也使用原始的 Integer 类。 It does l.set(i, l.set(j, l.get(i)));它做l.set(i, l.set(j, l.get(i))); in order to swap - as set returns the previous element at that position.为了交换 - as set返回该位置的前一个元素。 The JIT compiler can probably unwrap set and utilize that previous element immediately. JIT 编译器可能会立即解开 set 并使用前一个元素。

There is a Java function to do the job:有一个 Java 函数可以完成这项工作:

Collections.shuffle( list );

This should be significantly faster than a for loop.这应该比for循环快得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM