java - 数组应该比 ArrayList 快这么多吗?

标签 java arrays arraylist time-complexity benchmarking

我实现了两种方法,shuffleListshuffleArray它们使用完全相同的功能。我有一个 50 万个整数的 ArrayList,以及一个同样的 50 万个整数的数组。在我的基准测试代码中,它在相应的数组或 ArrayList 上执行每个方法 100 次并记录时间,它看起来像 shuffleArray shuffleList 大约需要 0.5 秒大约需要 3.5 秒,尽管代码没有使用任何 ArrayList 方法,而是使用 get 和 set,这应该与在数组中工作一样快。

现在我知道 ArrayList 有点慢,因为它们内部使用数组,但有一些额外的代码,但是它有这么大的区别吗?

     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;
        }
    }

基准测试代码:

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();
    }

我注释掉 for 循环中的一个方法并使用另一个。

更新:

我做了你们很多人建议的改变Int aInteger ashuffleList方法,并且它使它更快一点,现在是 3 秒而不是 3.5 秒,但我仍然认为这是一个很大的区别。

值得一提的是,将 shuffleArray 中的 int[] arr 更改为 Integer[] arr使用 keep int a 的方法来模拟数组的装箱和拆箱时间实际上会使其慢很多,它需要大约 3 秒,所以我可以使数组像 ArrayList 一样慢,但我不能这样做相反。

更新:

shuffleList 中使用 Collections.swap()确实使它与数组一样快,但我仍然不明白为什么,我的基准测试太有意义还是真的很重要?

最终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);
        }
    }

最佳答案

使用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);
    }

Integer 对象使用更多内存。


@Andy Turner 提到了现有的 Collections#swap。

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

如果没有 JIT 编译器的预热,这可能会降低基准测试速度, 但在生产代码中看起来会更好。不过无论如何,您可能都会使用Collections.shuffle


正如评论所述,交换版本也很快。首先,OP 展示了使用正确的微基准测试代码。

swap 也使用原始的 Integer 类。它执行 l.set(i, l.set(j, l.get(i))); 以便交换 - 因为 set 返回该处的前一个元素位置。 JIT 编译器可能可以立即解开集合并利用之前的元素。

关于java - 数组应该比 ArrayList 快这么多吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64766430/

相关文章:

java - Libgdx 停止当前正在播放的声音并播放新的声音

java - 存储与映射中的键相对应的多个值

php - 检查数据库数组中是否存在值不起作用

c - 生成数组中的随机数并计算平均值、最大值、最小值、总和

java - 实现 ArrayLists 到 Table 模型

java - 使用递归 Java 查找列表中的最大数字

java - Java 方法注释如何与方法覆盖结合使用?

python - 保留满足两个或多个条件的 numpy 数组的值

arrays - 查找、计数并索引数组中的重复项

java - 在停止并重新启动我的应用程序时,我收到 JMX 地址已在使用中的错误