Why are two AtomicIntegers never equal?

前端 未结 9 2130
日久生厌
日久生厌 2020-12-01 18:02

I stumbled across the source of AtomicInteger and realized that

new AtomicInteger(0).equals(new AtomicInteger(0))

evaluates to

相关标签:
9条回答
  • 2020-12-01 18:32

    equals is not only used for equality but also to meet its contract with hashCode, i.e. in hash collections. The only safe approach for hash collections is for mutable object not to be dependant on their contents. i.e. for mutable keys a HashMap is the same as using an IdentityMap. This way the hashCode and whether two objects are equal does not change when the keys content changes.

    So new StringBuilder().equals(new StringBuilder()) is also false.

    To compare the contents of two AtomicInteger, you need ai.get() == ai2.get() or ai.intValue() == ai2.intValue()

    Lets say that you had a mutable key where the hashCode and equals changed based on the contents.

    static class BadKey {
        int num;
        @Override
        public int hashCode() {
            return num;
        }
    
        @Override
        public boolean equals(Object obj) {
            return obj instanceof BadKey && num == ((BadKey) obj).num;
        }
    
        @Override
        public String toString() {
            return "Bad Key "+num;
        }
    }
    
    public static void main(String... args) {
        Map<BadKey, Integer> map = new LinkedHashMap<BadKey, Integer>();
        for(int i=0;i<10;i++) {
            BadKey bk1 = new BadKey();
            bk1.num = i;
            map.put(bk1, i);
            bk1.num = 0;
        }
        System.out.println(map);
    }
    

    prints

    {Bad Key 0=0, Bad Key 0=1, Bad Key 0=2, Bad Key 0=3, Bad Key 0=4, Bad Key 0=5, Bad Key 0=6, Bad Key 0=7, Bad Key 0=8, Bad Key 0=9}
    

    As you can see we now have 10 keys, all equal and with the same hashCode!

    0 讨论(0)
  • 2020-12-01 18:33

    AtomicInteger inherits from Object and not Integer, and it uses standard reference equality check.

    If you google you will find this discussion of this exact case.

    0 讨论(0)
  • 2020-12-01 18:38

    This is partly because an AtomicInteger is not a general purpose replacement for an Integer.

    The java.util.concurrent.atomic package summary states:

    Atomic classes are not general purpose replacements for java.lang.Integer and related classes. They do not define methods such as hashCode and compareTo. (Because atomic variables are expected to be mutated, they are poor choices for hash table keys.)

    hashCode is not implemented, and so is the case with equals. This is in part due to a far larger rationale that is discussed in the mailing list archives, on whether AtomicInteger should extend Number or not.

    One of the reasons why an AtomicXXX class is not a drop-in replacement for a primitive, and that it does not implement the Comparable interface, is because it is pointless to compare two instances of an AtomicXXX class in most scenarios. If two threads could access and mutate the value of an AtomicInteger, then the comparison result is invalid before you use the result, if a thread mutates the value of an AtomicInteger. The same rationale holds good for the equals method - the result for an equality test (that depends on the value of the AtomicInteger) is only valid before a thread mutates one of the AtomicIntegers in question.

    0 讨论(0)
提交回复
热议问题