This discussion is archived
3 Replies Latest reply: Oct 19, 2010 4:11 AM by 800268 RSS

Strange TreeMap behavior with null keys

805592 Newbie
Currently Being Moderated
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

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points