【问题标题】:How to write the clear() method in the list data structure?如何在列表数据结构中编写 clear() 方法?
【发布时间】:2017-08-22 09:52:33
【问题描述】:

我最近阅读了一些框架源代码,并注意到他们编写了类似列表的数据结构的 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;

直接实例化一个新的原生数组类型。

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

【问题讨论】:

标签: java data-structures


【解决方案1】:

我想动机是重新使用现有的支持数组,而不是分配一个新的。这一点很重要,尤其是当后备数组非常大时(在极少数情况下,这甚至可能意味着在旧数组被垃圾回收之前无法分配新数组)。

分配一个新数组(并对旧数组进行垃圾回收)可能比迭代现有数组并将所有元素的引用设置为null 更耗时。

编辑:如 cmets 中所述,在基于 List 的数组中设置对 null 的引用是不够的。您还必须指出List 是空的。在java.util.ArrayList 中,这是通过将size 属性设置为0 来完成的。

【讨论】:

  • 将所有元素设置为 null 不会清除集合。它只会更改元素值。你必须改变大小。将元素设置为 null 非常适合垃圾收集,但这既不是必要的,也不是充分的。
  • @EJP 问题是关于为什么将引用设置为 null 的循环是必要的,所以我觉得没有必要提及 size 属性。
  • @Eran btw 对于原语,在分配新数组时,您还要为数组归零付出代价;这增加了总价
  • @Eugene 你是指具有原始元素的集合吗?据我所知,java.util 中没有这样的集合。
  • @Eran 不,我的意思是一般;与此答案没有直接关系
【解决方案2】:

在某些情况下,清除和重用支持数组更有效。 (显然,您需要一个 size 字段,并且需要在清除列表时将其设置为零!)

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

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

  • 如果您在 ArrayList 上调用 clear,则后备数组的大小将保持与列表已满时相同的大小。如果列表很长,则数组可能会很大。如果您不再需要列表那么长,那么大数组会浪费大量空间。

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

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

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

简而言之,如果一个数组支持的列表很可能在多个 GC 循环中存活,那么清除(即清除和重用支持数组的过程)是一个可疑的命题。

【讨论】:

  • 你的最后一句话让我很困惑。您必须清除数组单元以防止内存泄漏(我们当然同意)。问题在于清除和重新使用数组,而不是简单地忘记它并创建一个新数组。
  • 我可能浏览文本有点太快了,但是是的:当你想重用数组时,清除是相当强制性的,所以我会使用单独的“重用”或“清除”和重用”。恕我直言,现在好多了。
  • OK ...我们称它为“寻址”。感谢您的反馈。
【解决方案3】:

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

还有一个事实是besides 分配一个数组可能太昂贵了并且清除以前的数组会更便宜,还有一个事实是以new byte[100] 的形式分配一个数组或任何必须做的内容归零,这也可能很昂贵。

因此在 java-9 中 String 连接使用 UNSAFE.allocateUninitializedArray 专门表示 Allocates an array of a given type, but does not do zeroing. 并且当然还添加:

此方法只应在极少数情况下使用,即高性能代码完全覆盖目标数组,并且编译器无法帮助消除归零。在绝大多数情况下,应该改用普通的 Java 分配。

这就是实际方法的样子:

    @ForceInline
    private static byte[] newArray(int length, byte coder) {
        return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
    }

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

【讨论】:

    【解决方案4】:

    同意@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而不是删除它。

    【讨论】:

    • 在不将大小设置为零的情况下将元素设置为 null 在语义上是不正确的。
    • removeAll 确实清除集合(通常),它只删除给定Collection&lt;?&gt; c 中的元素,但保留其他所有内容。跨度>
    • 确实如此。 removeAll 方法与提出的问题无关。
    • 问题清楚地表明将所有元素一一删除。这就是为什么我提到 removeAll
    • 问题询问哪个更好,使用新数组或将原始数组中的所有内容设置为null。你没有回答那个问题,也没有在答案中解释你为什么使用removeAll。我同意 removeAll 在这里不相关。
    【解决方案5】:

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

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

    【讨论】:

      【解决方案6】:

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

      【讨论】:

      • 我不是反对者,但是:ArrayList(将元素归零)和LinkedList(将节点之间的所有链接归零)目前都没有选择坚持严格要求的绝对最小值.因此,似乎需要考虑一些重要的性能因素,clear() 的运行时间只是其中一个方面(可能不是最重要的方面)。
      • 另一个问题。与其他操作结合使用时,将元素设置为 null 在语义上并非不正确。没有必要在其中摩擦 OP 的鼻子......因为明显的疏忽。
      • @StephenC 它本身在语义上是不正确的,这就是它在 OP 的 OP 中的呈现方式。这既不是必要的,也不是充分的。您选择的术语简直是奇怪的。
      • 什么?你不知道“在里面蹭别人的鼻子”是什么意思吗?在字典里查一下。而你选择的侮辱只是缺乏想象力!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-06
      • 1970-01-01
      相关资源
      最近更新 更多