繁体   English   中英

空的 ArrayList 和带有 null 元素的 ArrayList 之间的区别?

[英]Difference between an empty ArrayList and an ArrayList with null elements?

我正在为解析 JSON 的 REST 服务编写一些验证器,我发现了一些对我来说听起来很奇怪的东西(我根本不是JAVA 专家)。

考虑有两个ArrayLists

ArrayList<Object> list1 = new ArrayList<Object>();
ArrayList<Object> list2 = new ArrayList<Object>();

这两个列表有一些共同点:它们完全是空的(或充满 null 个元素)。 但如果我这样做:

list1.add(null);

虽然两者都完全是的,但它们的行为却完全不同 并使某些方法的结果大不相同

System.out.println(list1.contains(null));  //prints true!
System.out.println(list2.contains(null));  //prints false

System.out.println(CollectionUtils.isNotEmpty(list1));  //prints true
System.out.println(CollectionUtils.isNotEmpty(list2));  //prints false

System.out.println(list1.size());  //prints 1
System.out.println(list2.size());  //prints 0

做一些研究,看看这些方法中的每一个的实现,您可以确定这些差异的原因,但仍然不明白为什么区分这些列表是有效的或有用的。

  • 为什么add(item)不验证item!=null
  • 如果列表中全是空值,为什么contains(null)false

提前致谢!!!

编辑:

我基本上同意答案,但我还没有完全相信。 这是方法 remove 的实现:

/**
 * Removes the first occurrence of the specified element from this list,
 * if it is present.  If the list does not contain the element, it is
 * unchanged.  More formally, removes the element with the lowest index
 * <tt>i</tt> such that
 * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
 * (if such an element exists).  Returns <tt>true</tt> if this list
 * contained the specified element (or equivalently, if this list
 * changed as a result of the call).
 *
 * @param o element to be removed from this list, if present
 * @return <tt>true</tt> if this list contained the specified element
 */
public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}


/*
 * Private remove method that skips bounds checking and does not
 * return the value removed.
 */
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}

所以,现在如果我这样做:

ArrayList<Object> list = new ArrayList<Object>();
list.add(null);
System.out.println(list.contains(null)); //prints true!
list.remove(null);
System.out.println(list.contains(null));  //prints false!

我缺少什么?

包含null的列表不为 它包含null 列表允许包含null,因此如果需要,可以将null放入其中。

显式允许ArrayList并且能够存储null值,因为它们可能对您的程序有意义。 并且空列表是空的(即不包含任何内容,甚至不包含null 。在成功添加 (null)之后(它返回true表示信号成功),列表显然必须在contains (null)上返回true 。事实上,你甚至可以从该列表中删除 (null),它将再次为空。

幸运的是,您根本不必是Java专家或专家。

想到这一点的最佳方式就像一个停车场。 带有null元素的数组就像一个没有停放的停车场。 空阵列就像停车场的计划。

如果List包含零元素,则List为空。 这既是术语的自然含义,也是isEmpty()方法执行的测试。

null是任何ArrayList可以包含的值作为元素。 显然,在这种情况下,列表至少有一个元素,因此不是空的。 无论List的初始状态如何,如果该Listadd(null)调用正常完成,则之后列表不为空。 类似地,列表中的每个null元素都会影响其大小。

为什么add(item)不验证item!=null

约书亚布洛赫 他设计了它。 大多数人认为他是一个非常聪明的人,并认为Collections API非常成功。

说真的, List s可能包含null元素是一种设计选择。 (允许实现拒绝空值,但ArrayList作为通用实现接受它们)。

如果列表中充满空值,为什么包含(null)表示为false?

它没有。 不要只听我的话 - 你提出的数据也与你相矛盾。

整个混淆源于您创建的ArrayLists充满空值的错误初始假设。 它们不是,它们完全是空的 - 意味着它们没有元素 因此,如果对它们调用contains(null) ,则会得到false ,而当调用isEmpty()时,则为true

一旦你向list1添加了null,它就不再是空的,并且确实包含一个null元素 - list2仍然是空的,但仍然不包含null

在上一个代码示例中,对Collections.isNotEmpty的第二次调用肯定不会返回true 该列表有一个null元素,你删除它,所以它再次为空。

这里的主要外卖是:

  • 调用空构造函数或具有ArrayList初始容量的构造函数会创建一个空列表 此列表不包含任何元素,甚至不包含null
  • null是ArrayList的完全有效元素 这可能有用的情况并不常见,但确实存在。

区别在于 List 的大小,而不是它的容量 实例化 new ArrayList<>(5)时,初始容量设置为5 ,但大小保持为 0 ,因为我们没有添加任何元素。 如果我们调用add(null) ,只有元素size()会增加,并且遍历元素数组的所有函数将保持在 List 的大小之间,而不是capacity之间。

这样当我们需要快速添加新元素时,数组不会被复制到另一个大小为 + 1 的数组中,而是 Java 在大小等于它时将容量增加一半,以通过减少函数来提高性能执行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM