0
public static void main(String args[]) throws Exception {
    ConcurrentHashMap<byte[], Integer> dps =
         new ConcurrentHashMap<byte[], Integer>();

    System.out.println(dps.putIfAbsent("hi".getBytes(), 1));
    System.out.println(dps.putIfAbsent("hi".getBytes(), 1));
}

prints

null
null

Why doesn't it print a 1 on the second line? I have read the semantics for putIfAbsent and it should be guaranteed to work. (Note: this was distilled down from a large concurrent program... as you can see, it's now single-threaded.)

1 Answer 1

5

putIfAbsent() not working with ConcurrentHashMap

"hi".getBytes() is not a constant array so you are generating two different objects there. If you did something like the following you will see your 1.

byte[] bytes = "hi".getBytes();
System.out.println(dps.putIfAbsent(bytes, 1));
System.out.println(dps.putIfAbsent(bytes, 1));

The hashCode() and equals(...) methods on byte[] array are from Object which only looks at the reference of the object, not its contents.

Whenever you store something in a Map you need to make sure it overrides hashCode() and equals(...) methods unless you want to just compare references. This is a Java FAQ. See these docs: Java theory and practice: Hashing it out.

As @Mauren mentions in the comments, to use the contents of byte[] you are going to have to write a little class which wraps the byte[] and provides proper hashCode() and equals(...) methods. Or as @CostiCiudatu mentions, you can use a SortedMap and use a Comparator for byte[] which looks at the contents of the array.

As an aside, if String.getBytes() returns a new byte[] encoded with the StringCoding.StringEncoder class based on your character set, etc..

6
  • 1
    Just noticed if I change byte[] to String (and remove getBytes() of course), then I get the expected behavior. But my Strings are different objects, so by your logic it shouldn't work. Or do I not understand?
    – Fixee
    Commented Oct 1, 2013 at 20:00
  • Ok, based on your new edits, I understand: String compares contents, but byte[] compares references. Highly non-intuitive. Appears that using byte[] as a Map key is generally a bad idea, therefore.
    – Fixee
    Commented Oct 1, 2013 at 20:03
  • 1
    @Fixee String has overridden equals() and hashCode() methods, that's why it works correctly with String.
    – Jesper
    Commented Oct 1, 2013 at 20:03
  • 1
    Whenever you store something in a Map you need to make sure it overrides equals/hashCode unless you want to just compare references @Fixee. Java FAQ: ibm.com/developerworks/java/library/j-jtp05273/index.html
    – Gray
    Commented Oct 1, 2013 at 20:09
  • 2
    Or you can use a SortedMap (TreeMap, for instance) with a Comparator for byte arrays. This approach is used in HBase and it's suitable whenever creating wrapper objects for each byte array would be too expensive. Commented Oct 1, 2013 at 20:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.