【问题标题】:Best practice foreach new vs null最佳实践 foreach new vs null
【发布时间】:2015-01-12 08:17:07
【问题描述】:

我有一个方法可以每 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);
    }
}

【问题讨论】:

  • 第二个会导致 NullPointerException
  • 第二个会给你一个NPE。
  • 所以我必须使用new 清空ArrayList ?

标签: java memory-management arraylist


【解决方案1】:

第二个代码错误,会抛出 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而不指定初始容量会创建一个初始容量很小的列表,如果在新列表中添加许多元素,则必须多次调整大小,这也需要时间.

【讨论】:

  • 我明白了!非常感谢。
  • 还有一个进一步的建议,他可能想让callEveryFiveSeconds()方法synchronized
  • @eran 很好的解释。谢谢!
【解决方案2】:

您正在做的事情称为过早优化。如果您每 5 秒只调用一次方法,那么您应该正常编写代码,而不考虑微优化。如果您的程序在现代处理器上运行,那么它将能够在每个方法调用之间执行数百亿条指令。优化这一种方法不会对您的程序性能产生总体影响。内存方面,您的数组列表似乎只有 200 个项目长。在 64 位操作系统上,您的数组列表将需要不到 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);
}

【讨论】:

    【解决方案3】:

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

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

    ArrayList.clear()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-04
      • 2011-05-12
      • 1970-01-01
      • 2010-10-04
      • 2011-03-15
      • 1970-01-01
      • 2011-08-24
      • 2022-01-27
      相关资源
      最近更新 更多