Have you ever wondered why the java.util.Collections
class includes various "empty
" class methods, which return immutable empty collections and immutable empty iterators? This post answers this question.
An immutable empty list of flowers
The Collections
class's various "empty
" class methods offer a useful alternative to returning null
(and avoiding potential java.lang.NullPointerException
s from being thrown) in certain contexts. Listing 1 presents the source code to an application that demonstrates the benefit of an immutable empty list.
Listing 1. Empty and nonempty List
s of Flower
s
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
class Flowers
{
private List<String> flowers;
Flowers()
{
flowers = Collections.emptyList();
}
Flowers(String... flowerNames)
{
flowers = new ArrayList<String>();
for (String flowerName: flowerNames)
flowers.add(flowerName);
}
@Override
public String toString()
{
return flowers.toString();
}
}
public class EmptyListDemo
{
public static void main(String[] args)
{
Flowers flowers = new Flowers();
System.out.println(flowers);
flowers = new Flowers("Rose", "Violet", "Marigold");
System.out.println(flowers);
}
}
Listing 1 declares a Flowers
class that stores the names of various flowers in a list. This class provides two constructors: a noargument constructor and a constructor that takes a variable number of java.lang.String
arguments identifying various flowers.
The noargument constructor invokes <T> List<T> emptyList()
to initialize its private flowers
field to an empty java.util.List
of String
-- emptyList()
is a generic method and the compiler infers its return type from its context.
If you're wondering about the need for emptyList()
, examine the toString()
method. Notice that this method evaluates flowers.toString()
. If you didn't assign a reference to an empty List<String>
to flowers
, flowers
would contain the null
reference (the default value for this instance field when the object is created), and a NullPointerException
object would be thrown when attempting to evaluate flowers.toString()
.
Compile Listing 1 as follows:
javac EmptyListDemo.java
Run the resulting application as follows:
java EmptyListDemo
You should observe the following output:
[]
[Rose, Violet, Marigold]
An immutable empty iterator of to-do tasks
Most of the "empty
" methods return immutable empty collections, but three methods return immutable empty iterators: <T> Enumeration<T> emptyEnumeration()
(classic iteration method), <T> Iterator<T> emptyIterator()
, and <T> ListIterator<T> emptyListIterator()
. Listing 2 demonstrates the need for an immutable empty iterator.
Listing 2. NullPointerException
arising from the null reference in todoList
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
class ToDo
{
private String task;
private String date;
ToDo(String task, String date)
{
this.task = task;
this.date = date;
}
@Override
public String toString()
{
return task + ": "+ date;
}
}
class ToDoList
{
private List<ToDo> todoList;
void add(ToDo todo)
{
if (todoList == null)
todoList = new ArrayList<>();
todoList.add(todo);
}
public Iterator<ToDo> iterator()
{
return todoList.iterator();
}
}
public class EmptyIteratorDemo
{
public static void main(String[] args)
{
ToDoList todoList = new ToDoList();
//todoList.add(new ToDo("Mow the lawn", "April 5, 10 AM"));
Iterator<ToDo> todos = todoList.iterator();
while (todos.hasNext())
System.out.println(todos.next());
}
}
Listing 2 presents ToDo
and ToDoList
classes that describe individual and lists of to-do tasks in terms of task name and date by which the task should have been performed. ToDoList
's add()
method lazily initializes the list. Unfortunately, this lazy initialization can result in a thrown NullPointerException
object.
NullPointerException
arises when toDoList.iterator()
in ToDoList
's iterator()
method is executed and toDoList
contains the null reference because add()
hasn't been called. Compiling Listing 2 (javac EmptyIteratorDemo.java
) and running the application (java EmptyIteratorDemo
) results in the following output:
Exception in thread "main" java.lang.NullPointerException
at ToDoList.iterator(EmptyIteratorDemo.java:37)
at EmptyIteratorDemo.main(EmptyIteratorDemo.java:47)
You can avoid the exception by uncommenting the toDoList.add()
call in the main()
method. Better still, you can modify ToDoList
's iterator()
method to return the result of calling Collections.emptyIterator()
when toDoList
contains the null reference, as follows:
public Iterator<ToDo> iterator()
{
return (todoList != null) ? todoList.iterator()
: Collections.emptyIterator();
}
Conclusion
The Collections
class's various "empty
" class methods can help you write safer code that avoids thrown NullPointerExceptions
. They can also help you write more streamlined code because you don't always have to test for null references. For example, Listing 1's toString()
method specifies return flowers.toString();
instead of having to specify the longer return (flowers != null) ? flowers.toString() : "";
.
The following software was used to develop the post's code:
- 64-bit JDK 8u60
The post's code was tested on the following platform(s):
- JVM on 64-bit Windows 8.1
This story, "Immutable empty collections and iterators" was originally published by JavaWorld.