ArrayList与倍增数组大小?

时间:2015-11-16 14:01:07

标签: java arrays arraylist

因此,当数组无法保存我们想要的信息量时,通常的编程习惯是将数组的大小加倍。我们这样做是通过创建一个新数组,使用原始数组大小的两倍,用旧值填充这个新数组,并将其设置为旧数组。 这是Java中的一个例子:

public int[] doubleArray(int[] old) {
    int[] doubled = new int[old.length * 2];
    for (int i = 0; i < old.length; i++) {
        doubled[i] = old[i];
    }
    return doubled;
}

因此,使用标准数组并在需要时加倍大小或简单地使用ArrayList(根据您给出的输入改变自己的大小1),更多更好 ?基本上,当您需要存储多于数组允许的数量时,将数组大小加倍是否更好,或者每次增加其大小为1?一个人成为比另一个更好的选择的限制是什么?

如果我不得不猜测,使用标准数组应该更快,因为它在java.lang中,所以我做了下面的课来测试我的理论:

public class ArrayTesting {
    public static void main(String[] args) {
    long startTime1 = System.nanoTime();
    int[] ints = new int[10];
    for (int i = 0; i < ints.length; i++) {
      ints[i] = i * 2;
    }
    long timeToSetOriginalValues = (System.nanoTime() - startTime1);
    long startTime2 = System.nanoTime();
    ints = doubleArray(ints);
    long timeToDouble = (System.nanoTime() - startTime2);
    long startTime3 = System.nanoTime();
    for (int i = 9; i < ints.length; i++) {
      ints[i] = i * 2;
    }
    long timeToSetNewValues = (System.nanoTime() - startTime3);
    System.out.println("Set Values in " + timeToSetOriginalValues);
    System.out.println("Doubled Array in " + timeToDouble);
    System.out.println("Finished setting values in " + timeToSetNewValues);
    System.out.println("Total time: " + (timeToSetOriginalValues + timeToDouble + timeToSetNewValues));
  }

  public static int[] doubleArray(int[] old) {
    int[] doubled = new int[old.length * 2];
    for (int i = 0; i < old.length; i++) {
      doubled[i] = old[i];
    }
    return doubled;
  }
}

但由于一个未知的原因,我的结果变得非常不同。从11,000到4,000之间的所有时间变化的总时间。假设这是我做错的事情,是否有人能更好地回答我的问题?

3 个答案:

答案 0 :(得分:1)

对,我调查了一下,结果如下:

我创建了一个新类来测试编辑数组的时间,以及编辑ArrayList的时间。让我们从ArrayTesting类开始。

public class ArrayTesting {
    private static long totalTotal = 0L;
    private static int[] ints = new int[10];
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            runTest();
        }
        System.out.println("Final Length of Array: " + ints.length);
        System.out.println("Total Time was: " + totalTotal);
    }
    private static void runTest() {
        long startTime = System.nanoTime();
        for (int i = 0; i < ints.length; i++) {
            ints[i] = i * 2;
        }
        ints = doubleArray(ints);
        for (int i = 9; i < ints.length; i++) {
            ints[i] = i * 2;
        }
        long testTime = System.nanoTime() - startTime;
        totalTotal = totalTotal + testTime;
    }

    private static int[] doubleArray(int[] old) {
        int[] doubled = new int[old.length * 2];
        for (int i = 0; i < old.length; i++) {
            doubled[i] = old[i];
        }
        return doubled;
     }
}

此课程的流程如下:

  1. 创建新的整数对象数组(是的,这很重要),长度为10。

  2. 对于五次迭代,将当前数组的所有索引设置为index * 2,然后将数组大小加倍,并使用各自的值index * 2填充新索引。

  3. 打印结果

  4. 按照相同的过程,我为ArrayList创建了一个测试类:

    import java.util.ArrayList;
    class ArrayListTester {
        public static ArrayList<Integer> arl = new ArrayList<Integer>();
        public static long totalTotal = 0L;
        public static void main(String[] args) {
            //Set initial size for ArrayList to 10
            while(arl.size() < 10) arl.add(0);
            //Setting the size was not timed.
            for (int i = 0; i < 5; i++) {
                runTest();
            }
            System.out.println("Total ArrayList size: " + arl.size());
            System.out.println("Total Time: " + totalTotal);
        }
        public static void runTest() {
            long startTime1 = System.nanoTime();
            int initialSize = arl.size();
            for (int i = 0; i < initialSize * 2; i++) {
                if (i < initialSize)
                    arl.set(i, ((Integer) i * 2));
                else
                    arl.add((Integer) i * 2);
            }
            long totalForRun = System.nanoTime() - startTime1;
            totalTotal = totalTotal + totalForRun;
        }
    }
    

    如果您通读了这个类,您确实会发现它遵循相同的步骤,但是使用ArrayList可以使代码更加简洁。

    现在让我们来看看我们的结果......

    由于每个类在5次迭代中运行倍增大小的事物,因此我们最后的数组大小为320或10 * (2 ^ 5)(注意两个数组的起始大小为10)。但是,经过几次快速测试后,时间消耗就大不相同了。

    现在,连续5次运行ArrayTester类,并计算每次运行的平均时间(将其相加并除以运行次数),我收到的平均值为468,290纳秒,有些人可能会认为这是简单地将数组加倍是一个相当不错的组块,但只是等待......

    接下来,移动到ArrayListTester类并运行它5次以获得平均值,我收到的平均值恰好是2,069,230纳秒。

    如果您想亲自测试这些数字,我会在在线编译/解释器here上运行这些程序。

    最终结果:完成同一任务所需的ArrayList几乎长了4.5倍。

    现在回到最初提出的问题: &#34; 所以更好使用标准数组并在需要时加倍,或者只使用ArrayList?&#34;

    答案真正涉及您希望代码的效率。对于ArrayList,效率字面上不再存在,但代码的美学和组织显着增加。另一方面,如果您正在寻找一种更有效的处理方法,并且不关心您需要编写的代码,那么请使用数组。

答案 1 :(得分:0)

如果你看一下ArrayList实现,即使它有Array对象,当你调用add方法时,它也会确保容量,如果需要,增加大小像

这样的数组
elementData = Arrays.copyOf(elementData, newCapacity); 

其中elementData是存储数据的内部数组。

关于性能或内存消耗,因为它是具有许多功能的包装器,显然会与Array对象进行一些成本比较。

答案 2 :(得分:0)

简单的Array总是会更快,因为ArrayList是一个Array,当它在空间不足时创建一个新的Array,并在它到达时缩小它大。

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

代码取自OpenJDK7,您可以清楚地看到,简单的add方法所需的开销很小,即检查array的大小,如果它是小的还是大的,将array复制到更大的数组,然后设置元素。正常Array就可以了。

Arr[index] = value;

但是ArrayLists功能的增加以及代码质量的提升,在大多数非性能优化方案中都会远远优于传统的Array

作为研究,您可以在http://algs4.cs.princeton.edu/home/找到已经很好实施的Arrays调整大小的示例,其中包含调整大小Array http://algs4.cs.princeton.edu/13stacks/ResizingArrayStack.java.html的示例。