List
常用的List有ArrayList和LinkedList。
@Test
public void testList(){
List arrayList = new ArrayList();
arrayList.add(null);
arrayList.add(null);
System.out.println(arrayList.size());
List linkedList = new LinkedList();
linkedList.add(null);
linkedList.add(null);
System.out.println(linkedList.size());
}
打印结果:
2
2
由于ArrayList底层是数组,添加null并未对他的数据结构造成影响。LinkedList底层为双向链表,node.value = null
也没有影响。因此,它们都可以存储多个null。
Map
常用的Map有HashMap和TreeMap。针对Map,由于存储的是键值对,我们主要关注key能否为null。
@Test
public void testMap() {
Map<String, String> hashMap = new HashMap<>();
hashMap.put(null, "a");
hashMap.put(null, "b");
System.out.println(hashMap.size());
Map<String, String> treeMap = new TreeMap<>();
treeMap.put(null, "a");
treeMap.put(null, "b");
System.out.println(treeMap.size());
}
打印结果:
HashMap中最多只有一个key == null
的节点,因为key相同时,后面的节点会替换之前相同key的节点,所以HashMap是可以添加key == null 的节点的,只不过只会存在一个,put方法返回旧值。TreeMap的put方法会调用compareTo方法,对象为null时,会报空指针。
针对HashMap存null节点,这里可以再多说一点。HashMap 允许插入键为 null 的键值对,但是因为无法调用 null 的 hashCode() 方法,也就无法确定该键值对的桶下标,只能通过强制指定一个桶下标来存放。
在jdk1.8版本下,处理如下:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
也就是说,key为null时,HashMap 使用第 0 个桶存放键为 null 的键值对。这也解释了HashMap为什么只能存放一个key为null的节点。
Set
常用的Set有HashSet和TreeSet。
@Test
public void testSet(){
Set<String> hashSet = new HashSet();
hashSet.add(null);
hashSet.add(null);
System.out.println(hashSet.size());
Set<String> treeSet = new TreeSet<>();
treeSet.add(null);
treeSet.add(null);
System.out.println(treeSet.size());
}
打印结果:
HashSet底层是HashMap,add方法调用的是HashMap的put方法,也只能有一个null。
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
TreeSet底层是TreeMap,add方法调用的是TreeMap的put方法,会报空指针异常。
总结:
- List都可以添加null元素
- HashMap可以有1个key为null的元素,TreeMap不能有key为null的元素
所以HashSet底层是HashMap,可以有1个null的元素,TreeSet底层是TreeMap,不能有key为null的元素。
除了上述介绍的之外,集合家族还有很多其它的类型。如下面两张图。
【手动分割图片】
【手动分割图片】
这里补充三种集合类型。
【1】Vector 底层是数组,所以不会管你元素的内容是什么,可以存储多个null
【2】HashTable无论是key为null,还是value为null,都会报错。
Hashtable都是支持并发的,这样会有一个问题,map.get(key) 的返回结果是null,那么是因为不存在对应的key是null呢,还是key对应的value就是null。
HashMap是非并发的,可以通过contains(key)来做这个判断。而支持并发的Map在调用m.contains(key)和m.get(key),m可能已经不同了。
HashTable是很早的一个类了,JDK已经并不推荐使用。在并发场景下,更建议使用ConcrrentHashMap
【3】ConcurrentHashmap,同Hashtable。