215

I try with a loop like that

// ArrayList tourists

for (Tourist t : tourists) {
    if (t != null) {     
        t.setId(idForm); 
    }   
}

But it isn't nice. Can anyone suggest me a better solution?


Some useful benchmarks to make better decision:

While loop, For loop and Iterator Performance Test

cellepo
  • 4,001
  • 2
  • 38
  • 57
Juan de Dios
  • 2,683
  • 2
  • 20
  • 24
  • 2
    use `Iterator`? Dig java-doc. http://download.oracle.com/javase/6/docs/api/java/util/Iterator.html#remove%28%29 – Nishant Jan 27 '11 at 17:22
  • Because of your benchmarking reference, it appears you are defining 'nice'/'better' to be benchmarked "efficiency". And your reference itself seems to conclude with the Answer: "The iterator loop is the slowest, and the difference between for loop and while loop isn’t that significant." – cellepo Dec 15 '21 at 20:42

18 Answers18

393

Try:

tourists.removeAll(Collections.singleton(null));

Read the Java API. The code will throw java.lang.UnsupportedOperationException for immutable lists (such as created with Arrays.asList); see this answer for more details.

Community
  • 1
  • 1
Lithium
  • 5,102
  • 2
  • 22
  • 34
  • Small implementation detail: This call changes capacity of List. – Walery Strauch Nov 21 '14 at 18:51
  • 16
    Time complexity of `List.removeAll()` is **n^2**. Just saying. – Hemanth Feb 03 '16 at 14:05
  • 9
    For Java 8 or later, see @MarcG's answer below. – Andy Thomas Feb 06 '16 at 15:16
  • 3
    @Hemanth Can you elaborate on how you got that time complexity? Because it looks quite `O(n)` to me for both `ArrayList` and `LinkedList`. – Helder Pereira Aug 11 '16 at 20:31
  • 1
    @HelderPereira I don't think it should be _for this case_, since [the source](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/AbstractCollection.java#AbstractCollection.removeAll(java.util.Collection)) (line 349) seems to loop through _both lists_ (`contains()` loops the entire array) and since the `singleton` is only one element it would be `N * 1 = N`. However generally it would be `N^2`. – Salem Jan 04 '17 at 20:34
  • 3
    @1blustone `N^2` doesn't make sense here because the size of both collections are not related; ultimately it would be `N*M`, but that's not always the case. `ArrayList` overrides the method definition that you linked to relieve cost of doing multiple removes, which turns it into `N*T(c.contains)`; so if the collection in the parameter `c` is a `HashSet`, it will be `O(N)`; if it is a `TreeSet`, it will be `O(N*log M)`. The same time complexities apply for `LinkedList`, where they didn't have to put so much effort, because removes are cheap by definition (if you have the reference to the node). – Helder Pereira Jan 04 '17 at 23:47
  • 12
    @Hemanth No it isn't. It's n*m where m is the number of elements in this case a singleton of null which is 1. It's O(n). You can see the source code here and see it does read and write over the list one time, moving the elements to account for the remvoed one. – Tatarize May 28 '17 at 02:25
  • @Lithium note that Arrays.asList is not *immutable* but just fixed-length. immutable would also mean thread-safety, which a fixed-size list doesn't offer. – Gewure Sep 13 '17 at 12:54
147

As of 2015, this is the best way (Java 8):

tourists.removeIf(Objects::isNull);

Note: This code will throw java.lang.UnsupportedOperationException for fixed-size lists (such as created with Arrays.asList), including immutable lists.

Marcelo Glasberg
  • 29,013
  • 23
  • 109
  • 133
  • 2
    "Best" in what way? Is it faster than other approaches? Or is it just more readable by virtue of brevity? – Andy Thomas Feb 05 '16 at 22:30
  • 19
    Not only because of brevity, but because it's more expressive. You can almost read it: "From tourists, remove if object is null". Also, the old way is creating a new collection with a single null object, and then asking to remove the contents of a collection from the other. Seems a bit of a hack, don't you think? Regarding speed, you have a point, if the list is really big and performance is a concern, I would suggest testing both ways. My guess would be that `removeIf` is faster, but it's a guess. – Marcelo Glasberg Feb 06 '16 at 04:28
  • 2
    `Arrays.asList` is not _immutable_. It's fixed sized. – turbanoff Dec 18 '16 at 12:29
  • @turbanoff yes, you are right, of course. It's fixed-size only, I'll update the answer. – Marcelo Glasberg Dec 19 '16 at 14:19
48
list.removeAll(Collections.singleton(null));

It will Throws UnsupportedException if you use it on Arrays.asList because it give you Immutable copy so it can not be modified. See below the code. It creates Mutable copy and will not throw any exception.

public static String[] clean(final String[] v) {
    List<String> list = new ArrayList<String>(Arrays.asList(v));
    list.removeAll(Collections.singleton(null));
    return list.toArray(new String[list.size()]);
}
AZ_
  • 21,688
  • 25
  • 143
  • 191
19

If you prefer immutable data objects, or if you just dont want to be destructive to the input list, you can use Guava's predicates.

ImmutableList.copyOf(Iterables.filter(tourists, Predicates.notNull()))
19

Not efficient, but short

while(tourists.remove(null));
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
7
 for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }
Mat Mannion
  • 3,315
  • 2
  • 30
  • 31
  • This can be more useful when you have to delete elements while traversing. Coincidence is I was nulling the elements than trying to use `removeAll(..null..)`. Thanks! – Mustafa Jul 13 '13 at 23:39
  • You might be better off setting the values to null then removing at the end. The batchRemove in removeAll transverses the list, with a read and write location and iterates the list once, moving the read but not the write when it hits a null. .remove() there might legit have to arraycopy the entire array each time it's called. – Tatarize May 27 '17 at 12:17
5

The Objects class has a nonNull Predicate that can be used with filter.

For example:

tourists.stream().filter(Objects::nonNull).collect(Collectors.toList());
Jad Chahine
  • 6,849
  • 8
  • 37
  • 59
JeffF
  • 59
  • 1
  • 2
  • 1
    Welcome to Stack Overflow. When answering questions, please try to add an explanation of your code. Please go back and edit your answer to include more information. – Tyler Jul 28 '16 at 20:03
5

Pre-Java 8 you should use:

tourists.removeAll(Collections.singleton(null));

Post-Java 8 use:

tourists.removeIf(Objects::isNull);

The reason here is time complexity. The problem with arrays is that a remove operation can take O(n) time to complete. Really in Java this is an array copy of the remaining elements being moved to replace the empty spot. Many other solutions offered here will trigger this issue. The former is technically O(n*m) where m is 1 because it's a singleton null: so O(n)

You should removeAll of the singleton, internally it does a batchRemove() which has a read position and a write position. And iterates the list. When it hits a null, it simply iterates the read position by 1. When they are the same it passes, when they are different it keeps moving along copying the values. Then at the end it trims to size.

It effectively does this internally:

public static <E> void removeNulls(ArrayList<E> list) {
    int size = list.size();
    int read = 0;
    int write = 0;
    for (; read < size; read++) {
        E element = list.get(read);
        if (element == null) continue;
        if (read != write) list.set(write, element);
        write++;
    }
    if (write != size) {
        list.subList(write, size).clear();
    }
}

Which you can explicitly see is an O(n) operation.

The only thing that could ever be faster is if you iterated the list from both ends, and when you found a null, you set its value equal to the value you found at the end, and decremented that value. And iterated until the two values matched. You'd mess up the order, but would vastly reduce the number of values you set vs. ones you left alone. Which is a good method to know but won't help much here as .set() is basically free, but that form of delete is a useful tool for your belt.


for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }

While this seems reasonable enough, the .remove() on the iterator internally calls:

ArrayList.this.remove(lastRet);

Which is again the O(n) operation within the remove. It does an System.arraycopy() which is again not what you want, if you care about speed. This makes it n^2.

There's also:

while(tourists.remove(null));

Which is O(m*n^2). Here we not only iterate the list. We reiterate the entire list, each time we match the null. Then we do n/2 (average) operations to do the System.arraycopy() to perform the remove. You could quite literally, sort the entire collection between items with values and items with null values and trim the ending in less time. In fact, that's true for all the broken ones. At least in theory, the actual system.arraycopy isn't actually an N operation in practice. In theory, theory and practice are the same thing; in practice they aren't.

Tatarize
  • 10,238
  • 4
  • 58
  • 64
5

Mainly I'm using this:

list.removeAll(Collections.singleton(null));

But after I learned Java 8, I switched to this:

List.removeIf(Objects::isNull);
סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
Maged Almaweri
  • 312
  • 4
  • 11
4

Using Java 8, you can do this using stream() and filter()

tourists = tourists.stream().filter(t -> t != null).collect(Collectors.toList())

or

tourists = tourists.stream().filter(Objects::nonNull).collect(Collectors.toList())

For more info : Java 8 - Streams

NotJavanese
  • 4,072
  • 1
  • 15
  • 15
Jad Chahine
  • 6,849
  • 8
  • 37
  • 59
  • 1
    This solution is working with Immutable copy i.e --> List listOfString = Arrays.asList("test1",null,"test"); ..... too ! Thanks – Anurag_BEHS May 22 '18 at 12:44
3

There is an easy way of removing all the null values from collection.You have to pass a collection containing null as a parameter to removeAll() method

List s1=new ArrayList();
s1.add(null);

yourCollection.removeAll(s1);
Sastrija
  • 3,284
  • 6
  • 47
  • 64
shiv
  • 31
  • 1
  • This worked the best for me. It also allows you to easily add more than one entry in your "filter array" that gets passed into the removeAll method of the original collection. –  Feb 27 '12 at 15:36
2

This is easy way to remove default null values from arraylist

     tourists.removeAll(Arrays.asList(null));  

otherwise String value "null" remove from arraylist

       tourists.removeAll(Arrays.asList("null"));  
1

We can use iterator for the same to remove all the null values.

Iterator<Tourist> itr= tourists.iterator();
while(itr.hasNext()){
    if(itr.next() == null){
        itr.remove();
    }
}
Viktor Mellgren
  • 4,318
  • 3
  • 42
  • 75
amit
  • 807
  • 6
  • 14
1

I used the stream interface together with the stream operation collect and a helper-method to generate an new list.

tourists.stream().filter(this::isNotNull).collect(Collectors.toList());

private <T> boolean isNotNull(final T item) {
    return  item != null;
}
Mabi
  • 441
  • 2
  • 6
  • 17
1

I played around with this and found out that trimToSize() seems to work. I am working on the Android platform so it might be different.

theblitz
  • 6,683
  • 16
  • 60
  • 114
  • 2
    According to the javadoc, `trimToSize` does not modify the contents of a `ArrayList`. If this is different in android, it's probably a bug. – fabian Oct 18 '16 at 13:26
0

Using Java 8 this can be performed in various ways using streams, parallel streams and removeIf method:

List<String> stringList = new ArrayList<>(Arrays.asList(null, "A", "B", null, "C", null));
List<String> listWithoutNulls1 = stringList.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList()); //[A,B,C]
List<String> listWithoutNulls2 = stringList.parallelStream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList()); //[A,B,C]
stringList.removeIf(Objects::isNull); //[A,B,C]

The parallel stream will make use of available processors and will speed up the process for reasonable sized lists. It is always advisable to benchmark before using streams.

akhil_mittal
  • 23,309
  • 7
  • 96
  • 95
0

Similar to @Lithium answer but does not throw a "List may not contain type null" error:

   list.removeAll(Collections.<T>singleton(null));
Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
HannahCarney
  • 3,441
  • 2
  • 26
  • 32
0
List<String> colors = new ArrayList<>(
Arrays.asList("RED", null, "BLUE", null, "GREEN"));
// using removeIf() + Objects.isNull()
colors.removeIf(Objects::isNull);
cunhaf
  • 1,057
  • 1
  • 8
  • 12