是否有必要复制一份清单以确保安全?

k31*_*159 3 java collections list

Stream.toList的实现(和文档)是这样的:

Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())))
Run Code Online (Sandbox Code Playgroud)

我想知道为什么需要将返回的列表Arrays.asList复制到新的ArrayList. 仅返回以下内容还不够吗?

Collections.unmodifiableList(Arrays.asList(this.toArray()))
Run Code Online (Sandbox Code Playgroud)

我想知道,如果我编写一个返回它创建的列表的方法,如果我不费心制作它的防御性副本,是否会出现任何问题?

rzw*_*oot 9

它有一个目的

我想知道为什么 Arrays.asList 返回的列表需要复制到新的 ArrayList 中。仅返回以下内容还不够吗?

.toArray()我们在谈论哪一个?如果我们谈论的是来自 的那个,那么你是对的:该方法j.u.Collection的 javadoc保证它是一个新分配的数组,因此不需要制作防御性副本。然而,这不是这里涉及的:我们正在讨论's 。它的 javadoc 短得多:toArray()toArray()j.u.s.Stream.toArray()

    /**
     * Returns an array containing the elements of this stream.
     *
     * <p>This is a <a href="package-summary.html#StreamOps">terminal
     * operation</a>.
     *
     * @return an array, whose {@linkplain Class#getComponentType runtime component
     * type} is {@code Object}, containing the elements of this stream
     */
Run Code Online (Sandbox Code Playgroud)

不像j.u.Collection toArray(),此 javadoc 没有对该数组的性质做任何注释。因此,显然,javadoc 中的默认 implStream并不假定该数组一定是“安全”的(“安全”的意思是:以后不会被修改)。

我想你知道,但我要说得清楚一点:

Arrays.asList几乎总是一个错误。这是一个两全其美的列表:它在您传递给它的数组周围创建了一个轻包装器。所以,set()可行(因为foo[x] = value;这是可能的,而且确实是实现方式),但.add()不行(因为你无法更改数组的大小)。该列表不是一成不变的(即,如果将其传递给您直接控制之外的代码,您需要广泛记录,或者制作防御性副本),但也不是一个功能齐全的列表。使用List.of,它会生成完全不可变的列表 - 您可以将列表传递给您喜欢的任何人,而不必担心失去完整性控制。

编辑:并解释为什么它的Collections.unmodifiableList(new ArrayList<...) 而不是- make 上的那些静态方法在其中时抛出 NPE 的List.copyOf不可变列表,但流可以有Listnullnull ,但流可以有,所以你不能使用它。此编辑是由 @Rob Spoor 的评论带来的:)

不过,这并不像您想象的那么大问题

请注意,您找到的代码仅仅是默认实现 - 流的任何特定实现都可以自由地提出更好的方法来执行此操作。几乎您必然会遇到的每个实现都会覆盖定义。例如,如果您调用 arraylist's stream(),您最终会得到此实现,它来自streamops,并且与核心库中的所有集合使用的代码路径相同。因此,您最终使用的实际代码是这样的:

    @Override
    public List<P_OUT> toList() {
        return SharedSecrets.getJavaUtilCollectionAccess()
        .listFromTrustedArrayNullsAllowed(this.toArray());
    }
Run Code Online (Sandbox Code Playgroud)

这里的“可信数组”是指传递给它的数组保证不会在您的控制下发生变化。编译器无法强制执行这种保证,但是 arraylist(事实上,所有未损坏的集合,考虑到 javadoc 的j.u.Collection要求!)toArray()会创建新数组,因此这样做是安全的,而且效率更高(避免了一堆不必要的副本)。


归档时间:

查看次数:

246 次

最近记录:

1 年,11 月 前