如何在列表数据结构中编写clear()方法?

时间:2017-08-22 09:52:34

标签: java data-structures

我最近阅读了一些框架源代码,并注意到他们编写了类似于列表的数据结构的clear()方法。 逐个删除元素。

while (_arr.length > 0 )
{

    remove(_arr[0]);

}       

(也许上面看起来有点令人困惑,但它是因为这种语言本身的数组类型本身就是一个动态数组) 或

 for (int i = 0; i < size; i++)
  { elementData[i] = null;}
 size = 0;

但我记得我写过这样的代码。 该列表装饰了本机数组类型,我写了这样的clear()方法。

 _arr=new Array();
 _size=0;

直接实例化新的本机数组类型。

并且此代码使用具有垃圾回收的语言编写。 所以我认为所有的元素最终都会被收集,为什么需要一个循环呢?新的会快吗?

6 个答案:

答案 0 :(得分:20)

我想动机是重新使用现有的后备阵列,而不是分配新的。这一点非常重要,特别是当后备阵列非常大时(在某些极少数情况下甚至可能意味着在旧的数组被垃圾收集之前无法分配新数组)。

分配新数组(以及收集旧数组的垃圾)可能比迭代现有数组并将所有元素的引用设置为null更耗时。

编辑:正如评论中所提到的,在基于null的数组中设置对List的引用是不够的。您还必须指出List为空。在java.util.ArrayList中,可以通过将size属性设置为0来完成此操作。

答案 1 :(得分:7)

在某些情况下,清除和重用后备阵列更有效。 (显然,你需要一个size字段,清除列表时需要将其设置为零!)

  • 清除可以减少清空列表时产生的垃圾。
  • 如果逐步增加列表(即没有capacity提示),清除也会减少重新分配产生的垃圾。

但另一方面,clear并不总是一个好主意。例如;

  • 如果您在clear上调用ArrayList,则支持数组的大小将与列表已满时的大小相同。如果列表很长,则阵列可能非常大。如果你再也不需要这个列表,那么大阵列可能会浪费很多空间。

  • 无论如何,GC必须检查ArrayList的后备阵列中的每个单元格,与<{1}}字段的当前值无关。它需要将整个数组复制到“to”空间。

  • 这也意味着您需要将size分配给“已清除”的单元格,以避免内存泄漏。

  • 如果您不断将“新”对象放入大型“旧”数组中,可能会出现因代际和/或位置因素导致的次要性能问题。

简而言之,如果一个阵列支持的列表可能在多个GC周期中存活,那么清除(即清除和重用后备阵列的过程)是一个可疑的命题。

答案 2 :(得分:4)

应该是评论,但我认为它不适合。

还有一个事实是besides分配一个数组可能太贵了并且清除以前的数组会更便宜,还有一个事实就是以{{ {1}}或还必须归零内容,这也可能很昂贵。

因此在java-9中new byte[100]连接使用专门说String的{​​{1}},当然也会添加:

  

此方法仅应用于极少数情况下,高性能代码会完全覆盖目标数组,编译器无法帮助消除归零。在绝大多数情况下,应该使用正常的Java分配。

这就是实际方法的样子:

UNSAFE.allocateUninitializedArray

这只是为了证明在某些情况下,创建数组实际上太昂贵,而且不会更便宜。特别是因为数组可能需要连续的内存分配 - 但这不是规范要求的。

答案 3 :(得分:2)

同意@Eran关于创建新阵列或重新使用现有阵列的响应!

我想在这里再添加一个信息。

clear()的源代码如下:

public void clear() {
    modCount++;
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

removeAll()的源代码(在AbstractCollection中定义):

public boolean removeAll(Collection<?> c) {
    boolean modified = false;
    Iterator<?> e = iterator();
    while (e.hasNext()) {
        if (c.contains(e.next())) {
            e.remove();
            modified = true;
        }
    }
    return modified;
}

clear()当然更快,因为它不必处理所有这些额外的呼叫。因此,最好将元素设置为null而不是将其删除。

答案 4 :(得分:2)

在我看来,当创建一个新的数组并用零实例化时,它应该更慢。在这种情况下,完成初始化和迭代以设置默认值。在使用现有数组的情况下,仅完成迭代位。因此,使用现有数组并迭代它应该更快。

在我们的项目中,我们经常在长时间运行的批处理过程中的某些计算过程中创建数组对象。创建一个池后,您可以从中获取并返回使用后显示出显着的改进。

答案 5 :(得分:-1)

都不是。只需将_arr设置为空数组即可。出于效率原因,您当然不应在循环中调用remove(),并且将所有元素设置为null在语义上是不正确的。