java map集合存放null值问题
今天接到阿里的电话面试提到了一个问题是关于HashTable、HashMap的区别
以此为记~
java 集合能否加入null值
项目场景
剑指 Offer 09. 用两个栈实现队列
![](http://cdn-img.sm-tc.cn/?src=l4uLj4zF0NCWkpjRlZ2FldGckJLQmZaTmqCWkp6YmozQno2LlpyTmtDNz83Mzs7Qzc%2FNzM7Oz87Oz8%2FKy8nI0Y%2BRmA%3D%3D&restype=3&from=derive&pi=&v=1)
public int deleteHead() {
while(s2.isEmpty()){
//注意这里,我直接判断第二个栈空就加入第一个栈中的元素,没有判断第一个栈是否为空(当时想的是第一个栈为空不会加入元素或者抛出异常)
s2.offerFirst(s1.pollFirst());
}
if(s1.isEmpty()&&s2.isEmpty()){
return -1;
}
return s2.pollFirst();
}
问题描述
写力扣题时出现了这个问题,原代码在上面。
![](http://cdn-img.sm-tc.cn/?src=l4uLj4zF0NCWkpjRlZ2FldGckJLQmZaTmqCWkp6YmozQno2LlpyTmtDNz83Mzs7Qzc%2FNzM7Oz87Oz8%2FKy8nH0Y%2BRmA%3D%3D&restype=3&from=derive&pi=&v=1)
原因分析
尝试输出了下,发现总是返回空值,
1)认为输入栈(s1)为空会抛出异常,但是忘记了offer和poll针对异常不做处理,push和pop会抛出异常
![](http://cdn-img.sm-tc.cn/?src=l4uLj4zF0NCWkpjRlZ2FldGckJLQmZaTmqCWkp6YmozQno2LlpyTmtDNz83Mzs7Qzc%2FNzM7Oz87Oz8%2FKy8nG0Y%2BRmA%3D%3D&restype=3&from=derive&pi=&v=1)
2)跟着debug发现输出栈(s2)元素永远不为空,因为加入了s1的null,这就有意思了,java中有的集合可以加入null,有的不可以,所以顺便总结一下。
解决方案
public int deleteHead() {
while(s1.isEmpty()&&s2.isEmpty()){
return -1;
}
if(s2.isEmpty()){
while(!s1.isEmpty()){
s2.offerFirst(s1.pollFirst());
}
}
return s2.pollFirst();
}
1.List
可以看到ArrayList可以存储多个null,ArrayList底层是数组,添加null并未对他的数据结构造成影响。LinkedList底层为双向链表,node.value = null也没有影响。
@Test
public void testArrayList() {
ArrayList<Integer> list = new ArrayList<>();
list.add(null);
list.add(null);
System.out.println(list.size());//2
}
@Test
public void testLinkedList() {
LinkedList<Integer> list = new LinkedList<>();
list.add(null);
list.add(null);
System.out.println(list.size());//2
}
2.Set
Set底层是map,所以和map一样
3.Map
关于Map,HashMap中最多只有一个key == null的节点,因为key相同时,后面的节点会替换之前相同key的节点,所以HashMap是可以添加key == null 的节点的,只不过只会存在一个put方法返回旧值。
TreeMap的put方法会调用compareTo方法,对象为null时,会报空指针错。
@Test
public void testHashMap() {
HashMap<Integer, Integer> map = new HashMap<>();
map.put(null, null);
map.put(null, null);
System.out.println(map.size());//1
}
@Test
public void testTreeMap() {
TreeMap<Integer, Integer> map = new TreeMap<>();
map.put(null, null);
System.out.println(map.size());//NullPointerException
}
TreeMap源码
![](http://cdn-img.sm-tc.cn/?src=l4uLj4zF0NCWkpjRlZ2FldGckJLQmZaTmqCWkp6YmozQno2LlpyTmtDNz83Mzs7Qzc%2FNzM7Oz87Oz8%2FKy8nOz9GPkZg%3D&restype=3&from=derive&pi=&v=1)
4.Vector
Vector 底层是数组,所以不会管你元素的内容是什么,可以存储多个null
@Test
public void VectorTest(){
Vector box = new Vector();
box.add(null);
box.add(null);
Assert.assertEquals(2,box.size()); //ok
}
/**
*此为Vector的add函数
**/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
5.HashTable
HashTable底层为散列表,无论是key为null,还是value为null,都会报错
@Test
public void HashTableTest(){
Hashtable table = new Hashtable();
//table.put(new Object(),null); //Exception
//table.put(null,new Object()); //Exception
//table.put(null,null); //Exception
Assert.assertEquals(1,table.size());
}
//此为hashTable#put函数源码
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) { //value 需要判空,所以value不可为null
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode(); //key需要拥有实例去调用hashCode方法,所以也不能为空
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
至于为什么hashtable键和值都设计成不能为null,牛客网的一位大佬这样说到:
![](http://cdn-img.sm-tc.cn/?src=l4uLj4zF0NCWkpjRlZ2FldGckJLQmZaTmqCWkp6YmozQno2LlpyTmtDNz83Mzs7Qzc%2FNzM7Oz87Oz8%2FKy8nOztGVj5g%3D&restype=3&from=derive&pi=&v=1)
6.ConcurrentHashMap
看上面那张图
总结
结果:
- List都可以添加null元素
- HashMap可以有1个key为null的元素,TreeMap不能key为null的元素
- Set底层是Map,所以HashSet可以有1个null的元素,TreeSet不能有key为null的元素。
- -线程安全的类key和value都不能是null
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
最新评论