每个新与空的最佳实践

Best practice foreach new vs null

我有一个方法可以每 5 秒迭代一次大数组。在每个循环中,我向 ArrayList.

添加一些内容

我听说使用 new 需要更多的内存。

我需要知道 ArrayList 以上 empty/clear 的最佳方法是什么。

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

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

如果你想清除列表:

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

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

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

有一个名为 clear() 的方法可以清空数组列表:

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

ArrayList.clear()

你在做什么叫做过早优化。如果您每 5 秒只调用一次方法,那么您应该正常编写代码,而不考虑微优化。如果您的程序 运行ning 在现代处理器上,那么它将能够在每次方法调用之间执行数百亿条指令。优化这一种方法不会对您的程序性能产生总体影响。记忆方面,您的数组列表似乎只有 200 个项目。在 64 位 OS 上,您的数组列表将需要不到 2kB 的空间来容纳所有需要的内存资源。当现代计算机有千兆字节的可用内存时,在宏伟的计划中也没有多少。

实际上,与创建新的 ArrayList 相比,使用 ArrayList.clear 可能会为您的程序创建更多的工作。这是因为通过清除 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%,但仍然不够关心,因为您没有充分调用此方法。

使用 new 时线程同步的好处

那么为什么要使用这两种方式呢?使用新的 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);
}