【问题标题】:Helper to remove null references in a Java List?帮助删除 Java 列表中的空引用?
【发布时间】:2009-08-06 15:13:00
【问题描述】:

给定以下列表:

List<String> list = new ArrayList<String>();

list.add("s1");
list.add("s2");
list.add(null);
list.add("s3");
list.add(null);
list.add("s4");

我需要一个帮助类来删除空引用。比如:

SomeHelper.removeNullReference(list);

使得列表只包含“s1”、“s2”、“s4”、“s4”(非空引用)。

我应该用什么来满足这个要求?

【问题讨论】:

    标签: java collections


    【解决方案1】:
    list.removeAll(Collections.singleton(null));
    

    【讨论】:

    • 酷,我不知道 singletonList()。这比我的三班轮更优雅。
    • Collections 和 Array 类是真正的宝库——singletonList() 和 emptyList() 是在任何地方使用 ArraList 或 Vector 作为方法参数类型应该受到至少 20 鞭刑的主要原因.
    • 也可以是list.removeAll(Arrays.asList(null)),但singletonList 稍快。
    • 它向下兼容 pre-varargs Java 版本。
    • 编辑删除了四个不必要的字符,希望这不是错误的做法。
    【解决方案2】:

    Java 8 添加了Collection.removeIf(Predicate),它会删除与谓词匹配的所有元素,因此您可以使用

    从列表(或任何集合)中删除所有出现的null
    list.removeIf(Objects::isNull);
    

    使用java.util.Objects.isNull 作为Predicate

    如果您愿意,也可以使用.removeIf(x -&gt; x == null)the difference is very minor.

    【讨论】:

      【解决方案3】:

      如果您可以控制List 的实例化,那么您可以通过编写自定义List 实现来防止添加空值,例如:

      public class NoNullsList<E> extends ArrayList<E> {
      
        public void add(int index, E element) {
          if (element != null) {
            super.add(index, element);
          }
        }
      
        public boolean add(E e) {
          return e == null ? false : super.add(e);
        }
      }
      

      AFAIK,您不需要覆盖 addAll,因为其 ArrayList 实现调用 add

      List<String> list = new NoNullsList<String>();
      
      list.add("s1");
      list.add("s2");
      list.add(null);
      list.add("s3");
      list.add(null);
      list.add("s4");
      

      【讨论】:

      • 这些add 方法违反了List.add(或实际上是Collection.add)的约定。如果要禁止空元素,请在此处抛出异常(如 IllegalArgumentException),而不是返回 false
      • @PaŭloEbermann 当您创建一个类以明确忽略空值(从 OP 中可以看出不允许空值)时,在 (预期) 添加空值的情况。
      • @Thor84no ...然后你会从库代码中得到奇怪的失败,这些代码期望get返回空值它只是added,或者调用add三次增加size()由三个。 (并且您向库开发人员报告了一个错误,每个人的时间都被浪费了。)
      • @JeffreyBosboom 如果有人使用NoNullsList&lt;E&gt; 类并希望添加nulls,他们可能需要更仔细地阅读(也许应该将其设为final)。无论哪种方式,专门抛出异常并不能解决 OP 的问题。他要求一种忽略它们的方法,而不是抛出Exceptions。在这种情况下,nulls 是允许的,但应该被忽略 - 它们是预期的,而不是例外。随意提供一个替代方案,但是用可以积极打破这个作为 OP 解决方案的建议来解决这个问题是没有用的。
      • ArrayList.addAll 将调用add 的假设在 2011 年可能是正确的,我不知道,但现在,它肯定是错误的,这段代码不起作用;像list.addAll(Arrays.asList("foo", null, "bar", "baz")); 这样的简单语句会破坏它。所以这是一个很好的例子,为什么程序员不应该以这种方式子类化实现类。如果您想了解此反模式如何失败的其他示例,请查看 this one。您可以使用它来导出此方法将失败的其他情况,例如list.replaceAll(x -&gt; null);.
      【解决方案4】:

      Don 方法的一个缺点是它扩展自ArrayList。现在这可能已经足够好了,但是当您想使用不同的 List 实现时会发生什么?你可以创建一个NoNullsLinkedList 和一个NoNullsCopyOnWriteArrayList 等等,但最终你会得到一堆只在extends 子句中不同的小类。创建一个不接受空值的List 包装器可能会更好。例如:

      public class NonNullList<E> extends AbstractList<E> {
        private final List<E> delegate;
      
        public NonNullList(List<E> delegate) {
          this.delegate = delegate;
        }
      
        @Override
        public E get(int index) {
          return delegate.get( index );
        }
      
        @Override
        public int size() {
          return delegate.size();
        }
      
        @Override
        public E set(int index, E element) {
          return delegate.set( index, element );
        }
      
        @Override
        public void add(int index, E element) {
          if( element != null ) {
            delegate.add( index, element );
          }
        }
      
        @Override
        public E remove(int index) {
          return delegate.remove( index );
        }
      }
      

      这是更多的代码,但现在您可以在创建对象时灵活地选择List 实现。

      一个可能的问题是您仍然可以将nulls 插入到底层List 对象中。 Don 的方法没有同样的限制。

      【讨论】:

      • 和Don的回应一样,你的班级违反了List.add(或者实际上是Collection.add)的合同。当 add 方法返回(true 或 false)时,应保证该元素是与提供的元素相等的元素,而您的实现却没有。改为抛出异常,或至少在显着位置记录您违反合同。
      • 您正在覆盖 set 方法,但没有执行任何 null 检查,这使得整个事情变得毫无意义。但是正如@PaŭloEbermann 已经提到的那样,即使在修复此问题时,也会违反List 的一般合同。
      【解决方案5】:

      我真的很喜欢 Java 8 新的简洁解决方案,我相信您应该尽可能使用它。

      list.removeIf(Objects::isNull);
      

      另一方面,如果您正在寻找一个简单的实现来了解它是如何完成的,例如返回新的非空列表的帮助器类,您可以查看以下代码。它也可以接受泛型类型,为了简单起见,我在帮助类中使用了相同的集合类型(如问题中所示)。

      package removenullrefdemo;
      
      import java.util.ArrayList;
      import java.util.List;
      import java.util.Objects;
      
      class ListHelper {
          public static List<String> removeNullReference(List<String> list) {
              List<String> newList = new ArrayList<>();
      
              list.forEach(item -> {
                  if (item != null)
                      newList.add(item);
              });
      
              return newList;
          }
      }
      
      public class Main {
          public static void main(String[] args) {
              List<String> list = new ArrayList<String>();
      
              list.add("s1");
              list.add("s2");
              list.add(null);
              list.add("s3");
              list.add(null);
              list.add("s4");
      
              println("Items:");
              list.forEach(item -> println(item));
      
              // Remove null refs
              println("Items (after nulls removed):");
              boolean useJava8removeIf = false;
              if (useJava8removeIf) {
                  println("Using Java8 removeIf method with predicate.");
                  list.removeIf(Objects::isNull);
              }
              else {
                  println("Using our own helper.");
                  list = ListHelper.removeNullReference(list);
              }
      
              list.forEach(item -> println(item));
      
          }
      
          static void println(String msg) {
              System.out.println(msg);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多