3 Replies Latest reply: Oct 19, 2010 6:11 AM by 800268 RSS

    Strange TreeMap behavior with null keys

    805592
      I would like to have a TreeMap with a modified Comparator that allows null keys, with the semantics that a null value is less than all other values. However, I'm getting some surprises.

      Setting up the TreeMap is simple enough
      SortedMap<Integer,Integer> map = new TreeMap<Integer,Integer>(new Comparator<Integer>(){
      
                     @Override
                     public int compare(Integer arg0, Integer arg1) {
                          if (arg0 == null){
                               if (arg1 == null)
                                    return 0;
                               return -1;
                          }
                          if (arg1 == null)
                               return 1;
                          return arg0.compareTo(arg1);
                     }
                });
      Now I can insert with a null key and nothing bad happens:
                map.put(null, 1);
                System.out.println("contains the null key? "+map.containsKey(null));
                map.put(1, 2);
                map.put(2, 3);
      And now if I query the map, it's there using a subMap(null,3):
                for (Entry<Integer,Integer> entry : map.entrySet()){
                     System.out.println(entry.getKey() + " -> "+ entry.getValue());
                }
      Now for the weird, the entrySet() claims it's not empty, but the interator() claims in has no entries and will throw errors
                System.out.println("headMap entries: isEmpty()? "+ map.headMap(4).entrySet().isEmpty());
                System.out.println("headMap entries: hasNext? "+ map.headMap(4).entrySet().iterator().hasNext());
                //System.out.println("headMap entries: hasNext? "+ map.headMap(4).entrySet().iterator().next());
                System.out.println("headMap entries: size? "+ (map.headMap(4).entrySet().size()));
                System.out.println("headMap keys: isEmpty()? "+ map.headMap(4).keySet().isEmpty());
      I tried looking into the source code, but I can't reproduce any of this using the current OpenJDK source code. I guess I should just use the open source version of TreeMap but I just thought this was strange. Is this a bug that was fixed and will be updated with the next jdk?

      I'm not entirely sure what my point is anymore since I didn't realize this was fixed in OpenJDK until I started writing this, but it's strange and others might find it helpful.

      Here that code is all in one go:
                Comparator<Integer> compare = new Comparator<Integer>(){
      
                     @Override
                     public int compare(Integer arg0, Integer arg1) {
                          if (arg0 == null){
                               if (arg1 == null)
                                    return 0;
                               return -1;
                          }
                          if (arg1 == null)
                               return 1;
                          return arg0.compareTo(arg1);
                     }
                };
                NavigableMap<Integer,Integer> map = new MyTreeMap<Integer,Integer>(compare);
                System.out.println("1 vs 2: "+ compare.compare(1,2));
                System.out.println("1 vs null: "+ compare.compare(1,null));
                System.out.println("null vs null: "+ compare.compare(null,null));
                
                map.put(null, 1);
                System.out.println("contains? "+map.containsKey(null));
                map.put(1, 2);
                map.put(2, 3);
                
                System.out.println("first key: "+map.firstKey());
                System.out.println("All entries:");
                for (Entry<Integer,Integer> entry : map.entrySet()){
                     System.out.println("\t"+entry.getKey() + " -> "+ entry.getValue());
                }
                System.out.println("All keys:");
                for (Integer key : map.keySet()){
                     System.out.println("\tkey -> "+ key);
                }
                System.out.println("subMap entries:");
                for (Entry<Integer,Integer> entry : map.subMap(null,4).entrySet()){
                     System.out.println("\t"+entry.getKey() + " -> "+ entry.getValue());
                }
                System.out.println("headMap entries: isEmpty()? "+ map.headMap(4).entrySet().isEmpty());
                for (Entry<Integer,Integer> entry : map.headMap(4).entrySet()){
                     System.out.println("\t"+entry.getKey() + " -> "+ entry.getValue());
                }
                System.out.println("headMap entries: hasNext? "+ map.headMap(4).entrySet().iterator().hasNext());
                //System.out.println("headMap entries: hasNext? "+ map.headMap(4).entrySet().iterator().next());
                System.out.println("headMap entries: size? "+ (map.headMap(4).entrySet().size()));
                System.out.println("headMap keys: isEmpty()? "+ map.headMap(4).keySet().isEmpty());
                for (Integer key : map.headMap(4).keySet()){
                     System.out.println("\tkey -> "+ key);
                }
      Edited to combine all the code in one place.

      Edited by: inspired2apathy on Oct 18, 2010 7:24 PM