原文链接:https://www.javaworld.com/article/3103442/learn-java/immutable-empty-collections-and-iterators.html
你是否曾经有过这样的疑问:为什么 java.util.Collections 类会包含各类"empty"的类方法并且这些方法会返回不可变空集合以及不可变空迭代器?这篇文章将会揭示答案。
为什么要返回不可变的空集合以及迭代器?
返回不可变的空集合以及空迭代器能够保证在多线程上下文中安全地被调用。
一个不可变的空List
Collections类中各类"empty"的类方法在某些特定的上下文中为我们提供返回null的选项(并且有效地避免抛出潜在的java.lang.NullPointerException异常)。下列源代码展示了一个不可变空list的好处:
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);
}
}
上述代码声明了一个Flowers类,这个类在一个list中存储了多个flower。这个类提供了两个构造方法:一个无参构造方法以及一个带有代表多种flowers的java.lang.String参数的构造方法。
在Flowers类中,无参构造方法通过调用 <T> List<T> emptyList() 方法将该对象的私有作用于初始化成String类型的java.util.List集合--emptyList() 方法是一个通用方法,编译器会根据上下文自行推断返回类型。
如果你怀疑emptyList()方法的必要性,可以测试下toString()方法。注意该方法等同于flowers.toString()方法。如果你不为flowers的空List<String>指定任何引用,flowers会包含空引用并且当你试图调用flowers.toString()时一个NullPointerException对象会被抛出。
运行上述代码你能够得出以下结果:
[]
[Rose, Violet, Marigold]
待办任务的不可变空迭代器
大多数"empty"方法返回不可变的空集合,但是这里存在3个方法返回不可变的空迭代器<T> Enumeration<T> emptyEnumeration(), <T> Iterator<T> emptyIterator(), 以及<T> ListIterator<T> emptyListIterator()。下列代码显示了不可变的空迭代器的必要性:
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());
}
}
上述代码中,ToDo和ToDoList类定义了一个单独的方法来输出任务的名称和任务执行的时间。ToDoList的add()方法以懒加载的形式初始化list。不幸的是,这种方式会导致NullPointerException异常对象的抛出。
当在ToDoList的iterator()中的toDoList.iterator()方法被执行并且toDoList包含空引用,NullPointerException就会发生因为add()方法还未被调用过。运行上述代码会产生如下结果:
Exception in thread "main" java.lang.NullPointerException
at ToDoList.iterator(EmptyIteratorDemo.java:37)
at EmptyIteratorDemo.main(EmptyIteratorDemo.java:47)
你能够通过去掉toDoList.add()方法的注释符来避免该异常。更好的是,通过修改ToDoList的iterator()方法使之当toDoList包含空返引用时调用Collections.emptyIterator()方法。具体代码如下:
public Iterator<ToDo> iterator()
{
return (todoList != null) ? todoList.iterator()
: Collections.emptyIterator();
}
总结
Collections类的"empty"类方法能够帮助我们使我们的代码避免抛出空指针异常。同时,他们同样能够令我们的代码更加流畅因为我们不需一致测试并考虑空引用。