【问题标题】:Is there a standard Java List implementation that doesn't allow adding null to it?是否有一个标准的 Java List 实现不允许向其添加 null?
【发布时间】:2011-06-22 00:14:15
【问题描述】:

假设我有一个List,我知道我永远不想在其中添加null。如果我在其中添加 null,则意味着我犯了一个错误。所以每次我否则我会打电话给list.add(item),我会打电话给if (item == null) throw SomeException(); else list.add(item);。是否有一个现有的 List 类(可能在 Apache Commons 或其他东西中)可以为我做这件事?

类似问题:Helper to remove null references in a Java List? 但我不想删除所有空值,我想确保它们一开始就不会被添加。

【问题讨论】:

  • 你不能只是继承 List 类并添加你刚刚列出的功能吗?
  • 这是一个非常定制的要求; null 是一个完全可以容纳在容器中的值(在大多数情况下)。所以你可能需要推出自己的包装器。
  • @Nick 我可以,但我必须覆盖(至少,在我的脑海中)addaddAll,再加上一两个构造函数。这可能不会那么难,但我怀疑有人(Apache、Google ......)已经做到了,并且在某个地方发现了一个我可能会错过的微妙错误。
  • @Oli 好吧,如果集合不支持空值,Collections API 非常清楚地允许 Nullpointer 异常。很明显,API 会考虑这种情况并做出其他决定。
  • 我碰巧发现了这个:kiwidoc.com/java/l/p/commons-collections/commons-collections/… 这并没有真正回答我原来的问题,但有类似的目的。

标签: java list collections null


【解决方案1】:

注意——这里的几个答案声称通过包装一个列表并签入addaddAll 来解决您的问题,但他们忘记了您也可以通过其listIterator 添加到List .获得正确的受限列表具有挑战性,这就是为什么 Guava 有 Constraints.constrainedList 为您做这件事。

但在查看之前,请先考虑您是否只需要一个 不可变 列表,在这种情况下,Guava 的 ImmutableList 无论如何都会为您执行空值检查。如果您可以改用 JDK 的 Queue 实现之一,也可以这样做。

(空敌对集合的好列表:https://github.com/google/guava/wiki/LivingWithNullHostileCollections

【讨论】:

【解决方案2】:

使用 Apache Commons Collection:

ListUtils.predicatedList(new ArrayList(), PredicateUtils.notNullPredicate());

将 null 添加到此列表会引发 IllegalArgumentException。此外,您可以通过任何您喜欢的 List 实现来支持它,如有必要,您可以添加更多要检查的谓词。

一般的集合也是如此。

使用 Google Guava:

Constraints.constrainedList(new ArrayList(), Constraints.notNull())

将 null 添加到此列表会引发 NullPointerException

【讨论】:

    【解决方案3】:

    AFAIK,JDK 中没有可用的标准实现。但是,Collection spec 表示当集合不支持空值时应该抛出 NullPointerException。您可以使用以下包装器将功能添加到任何 Collection(您必须实现其他 Collection 方法才能将它们委托给包装的实例):

    class NonNullCollection<T> implements Collection<T> {
    
        private Collection<T> wrapped;
        public NonNullCollection(Collection<T> wrapped) {
            this.wrapped = wrapped;
        }
        @Override
        public void add(T item) {
            if (item == null) throw new NullPointerException("The collection does not support null values");
            else wrapped.add(item);
        }
        @Override
        public void addAll(Collection<T> items) {
            if (items.contains(null)) throw new NullPointerException("The collection does not support null values");
            else wrapped.addAll(item);
        }
        ...
    }
    

    【讨论】:

    • +1 是的,我也是这样做的——除了 addAll 中明显的输入错误。其他方法几乎复制和粘贴,所以没有太多工作。 +1
    • 考虑将IllegalArgumentException 作为 null 是一个非法参数。见stackoverflow.com/questions/3881/…
    • @Steve Kuo:我只抛出 NullPointerException,因为如果 Collection 不支持空值,那是 Collection javadoc 所说的抛出。
    • 这并不可怕,但它有很多像 public T get(int i) { return wrapped.get(i); } 这样乏味的代码,这就是为什么我希望像 Fatal 那样的单行答案。
    • 链接已损坏。
    【解决方案4】:

    如果可以创建一个列表并且不改变现有列表,则在 Java 9 中添加的List.of 在添加的元素为空时抛出 NPE

    【讨论】:

      【解决方案5】:

      您可以通过包装一个 List 的实例并提供 null-checked add 方法来做到这一点。换句话说,你的类只有几个方法和内部变量。

      如果您需要列表的全部功能,您可以考虑使用 Guava(以前是 Google Collections)ForwardingList

      另一种方法是扩展 List,但问题是您需要同时覆盖 addaddAll

      有趣的是,guava有一个standardAddAll,可以作为addAll的实现,至少解决了部分问题。

      【讨论】:

      • 当然,如果您使用 Guava,您可以(并且应该)写 Constraints.constrainedList(list, Constraints.notNull()),正如 Kevin B 在此处的回答中提到的那样。
      【解决方案6】:

      虽然这个问题有点像“委托”,但使用子类化要容易得多,因为您打算继承几乎所有相同的功能。

      class MyList extends List {
      
        //Probably need to define the default constructor for the compiler
      
        public add(Object  item) {
          if (item == null) throw SomeException();
          else super.add(item);
        }    
      
        public addAll(Collection c) {
          if (c.contains(null)) throw SomeException();
          else super.addAll(c);
        }
      }
      

      【讨论】:

      • 你也应该覆盖 addAll。
      • 见上面我对尼克的评论。此外,我将不得不扩展一个像 ArrayList 这样的具体类,这意味着如果我以后需要一个不允许空值的 LinkedList,我将不得不重新做所有的工作。所以我想,嗯,也许它应该只是一个包装现有列表的实用方法,有点像Collections.unmodifiableList。然后我想,等等,也许有人已经经历了这一切。
      • @MatrixFrog 查看我的回答。 Apache Commons Collection 通常为列表和集合提供谓词装饰器。你可以用你喜欢的 List/Collection 实现来支持它,它不仅限于检查 not null,当然它也可以用于 not null 检查。
      【解决方案7】:

      这应该可以工作

      List<String> list = Collections.checkedList(new ArrayList<String>(), String.class);
      try {
          list.add(null);
          throw new AssertionError("Expected a NPE");
      } catch (NullPointerException expected) {
          System.out.println("list.add(null); threw "+expected);
      }
      try {
          List<String> list2 = Arrays.asList("", null);
          list.addAll(list2);
          throw new AssertionError("Expected a NPE");
      } catch (NullPointerException expected) {
          System.out.println("list.addAll(\"\", null); threw " + expected);
      }
      

      但是 addAll 实现中的错误意味着您需要使用以下内容

      List<String> list = Collections.checkedList(
         Collections.checkedList(new ArrayList<String>(), String.class), String.class);
      

      你得到

      list.add(null); threw java.lang.NullPointerException
      list.addAll("", null); threw java.lang.NullPointerException
      

      【讨论】:

      • 这实际上不是add 实现中的错误吗?正如我在 mP 的回答中所说,文档说:“由于 null 被认为是任何引用类型的值,因此返回的列表允许在后备列表中插入 null 元素。” kiwidoc.com/java/l/p/java/j2se/1.6/p/java.util/c/Collections 所以在我看来add 是对的,addAll 是错的。
      • 我同意它与 javadoc 不一致。 typeCheck 代码只执行if(!type.isIntance(o)) 没有对null 进行特殊处理以允许它。就个人而言,这是我想要的行为。
      • 这个漏洞已经为人所知多年了;我怀疑他们会修复它。
      • @KevinBourrillion 我认为它已被报告修复。
      • 这不再有效。在 Java 11(也可能是早期版本)中,addaddAll 方法在使用 instanceof 之前显式检查并允许 null
      【解决方案8】:

      试试 Collections.checkedList()。

      【讨论】:

      • 我认为这没有帮助。 “由于 null 被认为是任何引用类型的值,因此返回的列表允许在后备列表中插入 null 元素。” kiwidoc.com/java/l/p/java/j2se/1.6/p/java.util/c/Collections
      • 此集合执行 isInstance 以防止添加空值。代码中的一个错误允许使用 addAll() 添加空值。解决方法是将集合包装两次。 ;)
      猜你喜欢
      • 2016-10-27
      • 2022-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-12
      • 2022-01-25
      • 2019-03-26
      相关资源
      最近更新 更多