最佳实践foreach new vs null

时间:2015-01-12 08:17:08

标签: java memory-management arraylist

我有一个方法,每5秒迭代一个大型数组。在每个循环中,我向ArrayList添加内容。

我听说使用new会占用更多内存。

我需要知道上面ArrayList清空/清除的最佳方法是什么。

private ArrayList<MyMarker> myArray;

public void callEveryFiveSeconds(){
    myArray = new ArrayList<MyMarker>(); //clears previous array data
    //grab array data from server
    for (int i = 0; i < 200; i++) {
       myArray.add(someData);
    }
}

private ArrayList<MyMarker> myArray  = new ArrayList<MyMarker>();

public void callEveryFiveSeconds(){
    myArray = null; //clears previous array data
    //grab array data from server
    for (int i = 0; i < 200; i++) {
       myArray.add(someData);
    }
}

3 个答案:

答案 0 :(得分:10)

第二个代码错误,将抛出NullPointerException。

如果要清除列表:

public void callEveryFiveSeconds(){
    myArray.clear();
    //grab array data from server
    for (int i = 0; i < 200; i++) {
       myArray.add(someData);
    }
}

关于哪个选项更好 - 创建新的ArrayList实例或清除现有实例 - 后者应该占用更少的内存,因为它不必为新的{{1}分配新数组}。前者将使用新数组分配新的ArrayList,旧的ArrayList将有资格进行垃圾回收。这意味着第二个选项(清除列表)将节省垃圾收集器的工作,这在性能方面也是一件好事。

如果在ArrayList的每次调用中添加到列表中的元素数量保持相似,则每次清除现有列表而不是创建新列表会有另一个好处。这样做的原因是创建新的callEveryFiveSeconds而不指定初始容量会创建一个初始容量较小的列表,如果向新列表中添加许多元素,则必须多次调整大小,这也是花费时间。

答案 1 :(得分:2)

您正在做的事情称为过早优化。如果您每5秒钟只调用一次方法,那么您应该正常编写代码,而不考虑微优化。如果您的程序在现代处理器上运行,那么它将能够在每个方法调用之间执行数百亿条指令。优化这一方法不会对程序的性能产生任何影响。记住,你的数组列表似乎只有200个项目。在64位操作系统上,阵列列表需要不到2kB才能保存所有必需的内存资源。当现代计算机拥有数十亿字节的内存时,在宏观计划中也没有多少。

实际上,您可以使用ArrayList.clear为您的计划创作更多作品,而不是创建新的ArrayList。这是因为通过清除ArrayList程序必须将后备阵列中的每个项目设置为null。但是,Java的垃圾收集器非常有效,当它处理一个未使用的ArrayList时,它能够在不先清除它的情况下处理后备阵列。

鉴于我的计算机上有以下微基准测试,每个基准测试运行一千万次:

public static final Object OBJ = new Object();
public static final int LENGTH = 200;

public static ArrayList<Object> clearArrayList(ArrayList<Object> objects) {
    objects.clear();
    for (int i = 0; i < LENGTH; i++) {
        objects.add(OBJ);
    }
    return objects;
}

public static ArrayList<Object> newArrayList() {
    ArrayList<Object> objects = new ArrayList<Object>(LENGTH);
    for (int i = 0; i < LENGTH; i++) {
        objects.add(OBJ);
    }
    return objects;
}

每次调用newArrayList的结果为870纳秒,每次调用clearArrayList的结果为970纳秒。因此,您可以看到使用新ArrayList的速度略快,但不足以关注。特别是当你谈论每5秒100 ns的差异时。您会注意到我为新运行预先调整了ArrayList的大小。当ArrayList没有预先调整大小时,平均调用时间为1690ns。虽然这个时间延长了50%,但仍然不足以关心,因为你没有足够的称呼这种方法。

使用新

时,线程同步会受益

那为什么要用这两种方式?使用新的ArrayList有一个好处,这就是它可以更好地与线程同步。如果您反复使用相同的ArrayList,则必须阻止其他线程在您进行变异时访问ArrayList。如果您使用新的ArrayList,则可以在将其用于其他线程访问之前,私下创建新的ArrayList

例如

private ArrayList<Object> objects = new ArrayList<Object>();
private final Object lock = new Object();

public ArrayList<Object> getObjects() {
    synchronized(lock) {
        return objects;
    }
}

public void setObjects(ArrayList<Object> newObjects) {
    synchronized(lock) {
        objects = newObjects;
    }
}

public void callEveryFiveSeconds() {
    // note that this method does not explicitly use synchronisation.
    // Synchronisation is only used when setting objects.
    ArrayList<Object> newObjects = new ArrayList<Object>();
    for (int i = 0; i < 200; i++) {
        newObjects.add(new Object());
    }
    setObjects(newObjects);
}

答案 2 :(得分:1)

有一个名为clear()的方法清空了一个arraylist:

public void callEveryFiveSeconds(){
    myArray.clear();
    //grab array data from server
    for (int i = 0; i < 200; i++) {
       myArray.add(someData);
    }
}

ArrayList.clear()