【问题标题】:Why is ArrayList's sort method faster than Arrays' in Java?为什么 ArrayList 的排序方法比 Java 中的 Arrays 快?
【发布时间】:2018-12-27 03:33:06
【问题描述】:

以下代码的目标是对 300,000 个 int 数字进行排序。我发现 ArrayList 的 sort() 的持续时间小于 Arrays 的 sort()。在内部,它们使用相同的算法进行排序。 ArrayList 使用 Arrays 的 sort() 对其元素数据进行排序。

public class EasySort {
    public static void main(String args[]) {
        // Read data from file, number split by ","
        FileReader fr = null;
        try {
            fr = new FileReader("testdata2.txt");
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        BufferedReader  bufferedReader=new BufferedReader(fr);
        String line=null;
        try {
            line=bufferedReader.readLine();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // use split method to generate a String array to save numbers
        String[] strArray=line.split(",");

        //Convert string array to ArrayList<Integer>
        ArrayList<Integer> integerList=new ArrayList<>();
        for(String str:strArray){
            integerList.add(Integer.parseInt(str));
        }

        //Sort by ArrayList
        long t0=System.currentTimeMillis();
        integerList.sort(((p1,p2)->(p1.intValue()<p2.intValue()?-1:p1.intValue()>p2.intValue()?1:0)));
        long t1=System.currentTimeMillis();
        System.out.println("ArrayList Sort duration:"+(t1-t0));

        //Convert string array to Integer array
        Integer[] integerArray=new Integer[strArray.length];
        int i=0;
        for(String str:strArray){
            integerArray[i++]=Integer.parseInt(str);
        }

        //Sort by Arrays
        t0=System.currentTimeMillis();
        Arrays.sort(integerArray, ((p1,p2)->(p1.intValue()<p2.intValue()?-1:p1.intValue()>p2.intValue()?1:0)));
        t1=System.currentTimeMillis();
        System.out.println("Arrays duration:"+(t1-t0));
    }
}

结果如下:
ArrayList 排序持续时间:211
数组持续时间:435

我查看了 ArrayList 的源代码。它在自己的排序方法中使用 Arrays.sort()。

 @Override
    @SuppressWarnings("unchecked")
    public void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

因此,在我看来,我的代码应该显示相同的持续时间。但是我尝试了很多次,结果都差不多。发生了什么?

Java 版本:8
操作系统:Windows 7

【问题讨论】:

  • 您使用的是哪个 Java 版本?
  • List&lt;Integer&gt; 可以在没有比较器的情况下进行排序(Integer 已经实现了Comparable);所以,integerList.sort(null) 可以。而且,是否需要创建Integer [] integerArray?使用原始int 的数组;并使用Arrays.sort(intArray)。您可能会看到不同的结果。
  • 您尝试了多少次?我用ArrayList&lt;Integer&gt;Integer[]在我的机器上测试过,它们几乎是同一时间。

标签: java arrays sorting arraylist


【解决方案1】:

他们应该是一样的,我不知道为什么你有不同的结果。下面是我的代码,我几乎有同样的时间。对于T[]ArrayList&lt;T&gt;,两者都会调用Arrays.sort(T[], ...),然后使用归并排序或时间排序。

Random rand = new Random();
Integer[] array = new Integer[300000];
for (int i = 0; i < array.length; i++)
    array[i] = rand.nextInt(array.length);  
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(array));    

long a = System.currentTimeMillis();
Arrays.sort(array, 0 ,array.length);
long b = System.currentTimeMillis();
list.sort(null);
long c = System.currentTimeMillis();

System.out.println(b - a);
System.out.println(c - b);

【讨论】:

  • 我已经运行了你的代码,b-a=446,c-d=91。我的 Java 版本是 8。你的是哪个?
  • @David.Ren 我得到了 220~240,我的 java 版本是 7。
【解决方案2】:

这是一个热身问题 - 但究竟为什么我不知道。

使用此代码:

public void test() {
    Integer[] a = randomData(10000000);

    ArrayList<Integer> integerList = new ArrayList<>();
    for (Integer i : a) {
        integerList.add(i);
    }

    long t0, t1;
    //Sort by ArrayList
    t0 = System.currentTimeMillis();
    integerList.sort(((p1, p2) -> (p1.intValue() < p2.intValue() ? -1 : p1.intValue() > p2.intValue() ? 1 : 0)));
    t1 = System.currentTimeMillis();
    System.out.println("ArrayList duration:" + (t1 - t0));


    //Sort by Arrays
    Integer[] integerArray = Arrays.copyOf(a, a.length);
    t0 = System.currentTimeMillis();
    Arrays.sort(integerArray, ((p1, p2) -> (p1.intValue() < p2.intValue() ? -1 : p1.intValue() > p2.intValue() ? 1 : 0)));
    t1 = System.currentTimeMillis();
    System.out.println("Arrays duration:" + (t1 - t0));

}

Random r = new Random(System.currentTimeMillis());

private Integer[] randomData(int n) {
    Integer[] a = new Integer[n];
    for (int i = 0; i < n; i++) {
        a[i] = r.nextInt();
    }
    return a;
}

并将这两种排序移动到我得到的不同顺序:

数组持续时间:4209

ArrayList 持续时间:4570

ArrayList 持续时间:6776

数组持续时间:4684

所以如果ArrayList 先排序,则需要更长的时间。

所以@AndyTurner 的评论是正确的——参考How do I write a correct micro-benchmark in Java?

Java 8 - Windows 10

【讨论】:

  • 如果是热身,为什么即使在数组之后,arraylist 数字也会更糟?还有一个有趣的行为——如果你减小数组大小,arraylist 排序在任何位置都会更快
  • JIT 和其他 Java 优化技术使得正确设置微基准变得非常困难。
【解决方案3】:

这是我使用 Java 8 尝试过的。请注意,有一个数组是 ints,一个数组是 Integers,ListIntegers。此外,Integer 数组使用Arrays.parallelSort() 进行排序。

import java.util.*;
import java.util.stream.*;
public class TestingSorts {
    public static void main(String[] args) {
        long t0 = 0L;
        long t1 = 0L;
        // Run this procedure 10 times
        for (int i = 1; i < 11; i++) {
            // Create an int array and Integer List filled with random numbers
            int [] intArray = IntStream.generate(() -> new Random().nextInt())
                        .limit(300_000)
                        .toArray();
            Integer [] integerArray = IntStream.generate(() -> new Random().nextInt())
                        .limit(300_000)
                        .boxed()
                        .toArray(n -> new Integer[n]);
            Integer [] integerArrayP = IntStream.generate(() -> new Random().nextInt())
                        .limit(300_000)
                        .boxed()
                        .toArray(n -> new Integer[n]);
            List<Integer> intList = IntStream.generate(() -> new Random().nextInt())
                        .limit(300_000)
                        .boxed()
                        .collect(Collectors.toCollection(ArrayList::new));
            // Sort the List and the arrays
            t0 = System.currentTimeMillis();
            intList.sort(null);
            t1 = System.currentTimeMillis();
            System.out.println(i + ") ArrayList<Integer> sort duration: " + (t1 - t0));
            t0 = System.currentTimeMillis();
            Arrays.sort(integerArray, Comparator.naturalOrder());
            t1 = System.currentTimeMillis();
            System.out.println(i + ") Integer[ ] sort duration: " + (t1 - t0));
            t0 = System.currentTimeMillis();
            Arrays.parallelSort(integerArrayP, Comparator.naturalOrder());
            t1 = System.currentTimeMillis();
            System.out.println(i + ") Integer[ ] PARALLEL sort duration: " + (t1 - t0));
            t0 = System.currentTimeMillis();
            Arrays.sort(intArray);
            t1 = System.currentTimeMillis();
            System.out.println(i + ") int[ ] sort duration: " + (t1 - t0));
        }
    }
}



结果(在 CORE i3 处理器上运行的 Windows 7 64 位操作系统上):

1) ArrayList<Integer> sort duration: 200
1) Integer[ ] sort duration: 424
1) Integer[ ] PARALLEL sort duration: 414
1) int[ ] sort duration: 136
2) ArrayList<Integer> sort duration: 143
2) Integer[ ] sort duration: 101
2) Integer[ ] PARALLEL sort duration: 56
2) int[ ] sort duration: 33
3) ArrayList<Integer> sort duration: 124
3) Integer[ ] sort duration: 118
3) Integer[ ] PARALLEL sort duration: 96
3) int[ ] sort duration: 42
4) ArrayList<Integer> sort duration: 108
4) Integer[ ] sort duration: 102
4) Integer[ ] PARALLEL sort duration: 92
4) int[ ] sort duration: 57
5) ArrayList<Integer> sort duration: 142
5) Integer[ ] sort duration: 113
5) Integer[ ] PARALLEL sort duration: 118
5) int[ ] sort duration: 31
6) ArrayList<Integer> sort duration: 113
6) Integer[ ] sort duration: 103
6) Integer[ ] PARALLEL sort duration: 58
6) int[ ] sort duration: 32
7) ArrayList<Integer> sort duration: 115
7) Integer[ ] sort duration: 116
7) Integer[ ] PARALLEL sort duration: 57
7) int[ ] sort duration: 33
8) ArrayList<Integer> sort duration: 115
8) Integer[ ] sort duration: 115
8) Integer[ ] PARALLEL sort duration: 58
8) int[ ] sort duration: 31
9) ArrayList<Integer> sort duration: 114
9) Integer[ ] sort duration: 101
9) Integer[ ] PARALLEL sort duration: 52
9) int[ ] sort duration: 32
10) ArrayList<Integer> sort duration: 113
10) Integer[ ] sort duration: 114
10) Integer[ ] PARALLEL sort duration: 57
10) int[ ] sort duration: 32

编辑:添加了对Integer 数组进行排序的功能。
编辑:添加了使用并行排序对Integer 数组进行排序的功能。

【讨论】:

  • 我认为,我们需要确保所有测量都基于相同的算法/方法。最后,intList.sort(null) 调用 ComparableTimSort.sort(),Arrays.sort(intArray) 调用 DualPivotQuicksort.sort()
  • List 可以使用并行流进行排序。数组没有并行流选项,只有并行排序。
猜你喜欢
  • 2014-12-24
  • 1970-01-01
  • 2021-01-21
  • 1970-01-01
  • 1970-01-01
  • 2018-03-28
  • 1970-01-01
  • 1970-01-01
  • 2021-06-19
相关资源
最近更新 更多