43

Given the following List:

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

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

I need a helper class that removes null references. Something like:

SomeHelper.removeNullReference(list);

such that the list only contains "s1", "s2", "s4", "s4" (non-null references).

What should I use to fulfill this requirement?

riddle_me_this
  • 8,575
  • 10
  • 55
  • 80
Arthur Ronald
  • 33,349
  • 20
  • 110
  • 136

5 Answers5

119
list.removeAll(Collections.singleton(null));
Kevin Bourrillion
  • 40,336
  • 12
  • 74
  • 87
Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
17

Java 8 added Collection.removeIf(Predicate) that removes all elements matching the predicate, so you can remove all occurrences of null from a list (or any collection) with

list.removeIf(Objects::isNull);

using java.util.Objects.isNull as a Predicate.

You can also use .removeIf(x -> x == null) if you prefer; the difference is very minor.

Jeffrey Bosboom
  • 13,313
  • 16
  • 79
  • 92
3

A drawback of Don's approach is that it extends from ArrayList. That may be good enough for now, but what happens when you want to use a different List implementation? You could make a NoNullsLinkedList and a NoNullsCopyOnWriteArrayList, etc, but then you wind up with a bunch of little classes that differ only in their extends clause. It might be better to create a List wrapper that doesn't accept null values. For example:

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 );
  }
}

It's more code, but now you have the flexibility of choosing a List implementation when you create the object.

A possible problem is that you can still insert nulls into the underlying List object. Don's approach doesn't have the same limitation.

debaser
  • 109
  • 2
  • 4
    As well as Don's response, your class violates the contract of `List.add` (or actually `Collection.add`). When the add method returns (either true or false), the element should be guaranteed to be an element equal to the supplied one, which your implementation doesn't. Throw an exception instead, or at least prominently document that you violate the contract. – Paŭlo Ebermann Jun 03 '13 at 17:30
  • You are overriding the `set` method, but not performing any `null` check, which makes the entire thing pointless. But as @PaŭloEbermann already mentioned, even when fixing this issue, it would violate the general contract of `List`. – Holger Jan 29 '19 at 17:47
3

If you can control the instantiation of the List then you could prevent the nulls being added by writing a custom List implementation such as:

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, you don't need to override addAll because the ArrayList implementation thereof calls 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");
Dónal
  • 185,044
  • 174
  • 569
  • 824
  • 2
    These `add` methods violates the contract of `List.add` (or actually Collection.add). If you want to forbid null elements, throw an Exception (like IllegalArgumentException) there instead of returning `false`. – Paŭlo Ebermann Jun 03 '13 at 17:26
  • 1
    @PaŭloEbermann When you create a Class for the express purpose of ignoring nulls (not disallowing nulls as you can see from the OP), it doesn't make any sense to throw an Exception in the (expected) case of a null being added. – Vala Aug 30 '13 at 14:43
  • @Thor84no ...and then you get strange failures from library code that expects to `get` back the nulls it just `add`ed, or that calling `add` three times increases `size()` by three. (And you report a bug to the library developers and everyone's time is wasted.) – Jeffrey Bosboom Mar 25 '16 at 23:27
  • 2
    @JeffreyBosboom If someone uses the class `NoNullsList` and expect the `null`s to be added, they probably need to read more carefully (perhaps it should be made `final`). Either way, throwing an exception specifically *does not solve the OP's problem*. He requested a way of ignoring them, not of having `Exception`s thrown. In this situation, `null`s are allowed, but should be ignored - they are expected, not exceptional. Feel free to provide an alternative, but shooting this down with suggestions that would actively break this as a solution for OP is not useful. – Vala Mar 26 '16 at 23:01
  • The assumption that `ArrayList.addAll` will call `add` might have been true back in 2011, I don’t know, but nowadays, it’s definitely wrong and this code does not work; a simple statement like `list.addAll(Arrays.asList("foo", null, "bar", "baz"));` will break it. So it’s a good example why programmers should not subclass implementation classes in that way. If you want other examples of how this anti-pattern can fail, look at [this one](https://stackoverflow.com/a/26841569/2711488). You can use this to derive other cases where this approach will fail, e.g. `list.replaceAll(x -> null);`. – Holger Jan 29 '19 at 17:42
2

I really like the Java 8's new concise solution, I believe you should use that whenever possible.

list.removeIf(Objects::isNull);

On the other side, if you are looking for a simple implementation to learn how it could be done, such as a helper class that returns new non-null list, you can take a look at the following code. It can also be made to accept generic types, for the sake of simplicity I used the same collection type (as in the question) in the helper class.

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);
    }
}
omt66
  • 4,765
  • 1
  • 23
  • 21