List、Set 和 Map 的值都可以为 null。
List
List 是一个有序的集合,可以包含重复的元素。在 Java 中,ArrayList 和 LinkedList 都是 List 的实现类。
ArrayList 在 Java 中是基于动态数组实现的。它的设计允许存储任何类型的对象,包括 null
。当一个 null
被添加到 ArrayList 中时,它只是作为数组的一个元素被存储,并不会影响到 ArrayList 的内部数据结构,因为数组本身不关心存储的是引用类型还是 null
。
List<String> list = new ArrayList<>();
list.add("Hello");
list.add(null);
LinkedList 在 Java 中是基于双向链表实现的。这意味着每个元素都是一个节点,每个节点包含数据和指向前后节点的引用。LinkedList 的设计允许每个节点的数据部分存储任何类型的对象,包括 null
。当一个节点的 value
被设置为 null
时,它只是改变了该节点所持有的数据,并不会影响到 LinkedList 的内部双向链表结构。
LinkedList<String> list = new LinkedList<>();
list.add(null);
list.add(null);
Set
Set 是一个不包含重复元素的集合。
HashSet底层是HashMap,可以有1个为null的元素。
Set<String> set = new HashSet<>();
set.add("World");
set.add(null);
LinkHashSet底层也是hashmap,允许存在一个为null的元素。
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("apple");
set.add(null);
TreeSet
在 Java 中是基于红黑树数据结构实现的。如果尝试将 null
作为节点插入到 TreeSet
中,就会违反红黑树相应规则。无法维护树的结构完整性,因此 TreeSet
在检测到 null
插入尝试时抛出异常,防止数据结构的损坏。
Map
Map 是一个键值对的集合,其中每个键都是唯一的。
HashMap
在 Java 中允许最多只有一个键为 null,多的null值进行覆盖,值可以存储多个null。
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", null);
TreeMap
在 Java 中是基于红黑树实现的,它要求每个键对象必须实现了 Comparable
接口或者提供了 Comparator
。当向 TreeMap
添加键值对时,put
方法会调用键的 compareTo
方法来确定其在树中的位置。
由于 null
不能被比较(即无法调用 null.compareTo(Object)
),尝试将键为 null
的元素添加到 TreeMap
中会导致 NullPointerException
。这是因为 TreeMap
需要根据键的比较结果来维护其有序性质,而 null
无法参与这种比较过程。
因此,与 HashMap
不同,TreeMap
不允许键为 null
的元素存在。如果尝试插入键为 null
的元素,将会抛出 NullPointerException
,如下例所示:
TreeMap<String, String> treeMap = new TreeMap<>();
treeMap.put(null, "value"); // 这里会抛出 NullPointerException
Hashtable
是一个古老的数据结构,它基于散列表实现,并且同步以支持多线程环境。与 HashMap
不同,Hashtable
不允许键或值为 null
。尝试插入键或值为 null
的元素会导致 NullPointerException
。
这种设计决策是出于早期的 Java 编程实践,当时可能更倾向于强制程序员避免使用 null
键或值,以确保数据的完整性和明确性。Hashtable
类是在 Java 集合框架出现之前设计的,而 HashMap
是后来随着集合框架的引入而设计的,它允许使用 null
键和值,提供了更多的灵活性。