该方法如果 key == null 会抛出异常,所以这个要注意,在添加的时候,如果比较器不为空的话,就根据比较器比较结果进行插入,如果比较为空,如果插入Key为空的
话,就直接异常,为什么会这样做呢,因为TreeMap是一个红黑树结构,其结构是根据键值的排序进行建立的,之所以比较器不为空的情况不抛异常,是因为
比较器中可能会有与NULL比较的情况,所以可能会有这种树中为结点Key为空的情况,还有就是如果Key=NULL作为根结点会直接插入,不会进行比较
但这里要注意了,如果树中有Key为NULL情况,在get(Object) 方法执行的时候是调用 getEntry(Object),我们来看一下getEntry(Object)源码:
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
final Entry<K,V> getEntryUsingComparator(Object key) {
K k = (K) key;
Comparator<? super K> cpr = comparator;
if (cpr != null) {
Entry<K,V> p = root;
while (p != null) {
int cmp = cpr.compare(k, p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
}
return null;
}
可见对于取方法也是分为两种情况,一种如果比较器为空,另一种比较器不为空,如果比较器为空的情况下,get(NULL)会直接异常,
所以这个要说的是,在比较器为空,且NULL作为根结点插入后,就不可能再取出这个Key=null 的值
下面是插入算法,至于红黑树的插入这里就不再细说了
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
// TBD:
// 5045147: (coll) Adding null to an empty TreeSet should
// throw NullPointerException
//
// compare(key, key); // type check
root = new Entry<K,V>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<K,V>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
TreeMap tree = new TreeMap();
tree.put(null,1);
System.out.println(tree.size());
System.out.println(tree.get(null));// 会抛异常
自定义Comparator
package Trees;
import static java.lang.System.out;
import java.util.TreeMap;
import java.util.Comparator;
public class TreeMapTest {
public static void main(String args[]) {
TreeMap<Integer,Integer> tree = new TreeMap<Integer,Integer>(new Com());
tree.put(1, 1);
tree.put(null, 2);
System.out.println(tree.size()); // 2
System.out.println(tree.get(null)); // 不会异常
}
}
class Com implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
if(o1==null&& o2==null)
return 0;
if(o1!=null)
return 1;
if(o2!=null)return -1;
return o1.compareTo(o2);
}
}
因此,在使用 TreeSet 过程中,如果是自定义的比较器中
在比较器方法 int compare(o1,o2)中,如果没有判断 o1,o2 与 null 所有关系的话,可能会造成 tree.put(null,1) 存入后无法 get(null) 的情况
如:我们只是简单的 if(o1==null || o2 == null) return -1 ;
那么 tree.put(1,1); tree.put(null,2)后, tree.get(null) 得到的值将会是空
说了这么多,就一句话,
如果要在 treeSet 中存放 Key = null 的键值,就要自定义 comparactor ,并且在自定义的 comparactor 中要指明 o1 , o2 与 null 的所有关系
如果不自定义 Comparactor 就不要存放 键值为 null 的值