0

In my java program, I have a map with a correlation between someID and data object of type MyClass.

ConcurrentMap<Integer, MyClass> a;

Less often but regularly, I have to check whether one of the "value" objects contain a certain long as variables b. Since I don't know the correlating someId to my otherId, I cannot access it via the map. To do so I would like to use containsValue:

a.containsValue(otherId)

To accomplish that I overwrote the hashCode and the equals function.

@Override
public boolean equals(Object o) {
    if ((o instanceof MyClass) && (((MyClass) o).someId().equals(this.someId()))) {
        return true;
    } else
    if ((o instanceof Long) && ((Long) o) == this.otherId) {
        return true;
    } else {
        return false;
    }
}

NOTE: The otherId in the code does not necessarily need to be the someID of the map.

The first if statement is symmetric, the second obviously not. I do understand that this is not nice, but writing a loop every time is not nice either. In this post they say a asymmetric equals will be ignored. How can I make java ignore the asymmetry problem?

Community
  • 1
  • 1
Jochen
  • 155
  • 1
  • 3
  • 15
  • 1
    equals has to be symmetric by definition and in this case you can't make it "ignore" the problem because you can't modify the `Long` class. if your key can be a `Long` just make it a Long. – Peter Lawrey Feb 29 '16 at 10:22
  • Calling `containsValue` on a Map is to be avoided where possible as it's a `O(n)` operation. – Peter Lawrey Feb 29 '16 at 10:23

3 Answers3

4

It you are using Java-8, it's not very hard to write:

a.values().stream().anyMatch(myObj -> myObj.otherId() == wantedId);
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
2

In short: don't try to do this using equals(). The contract on the method is that it should be symmetric (as well as reflexive, transitive and have a few other properties). See the Javadoc for a full description of the required properties of equals.

Classes in the Java API (and more generally) are implemented to expect this property to hold, and so may will behave unexpectedly if you intentionally violate the contract.

Add a new method to your classes which is explicitly for testing this assymetric property, e.g. compareAssymetrically.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
2

The problem with your equals implementation is that for a Long instance l and MyClass instance m, m.equals(l) may return true while l.equals(m) will always return false, since you can't change Long's implementation of equals.

If you use any classes that use equals (ArrayList, HashMap, etc...) you can't control whether m.equals(l) or l.equals(m) are called, so your code may not behave as you want it to behave.

If you want to check if a Long is "equal" to a MyClass instance, create a MyClass instance whose otherId is equal to that Long, and compare those two MyClass instances.

@Override
public boolean equals(Object o) {
    if (o instanceof MyClass) { 
        MyClass m = (MyClass) o;
        if (m.someId().equals(this.someId())) {
            return true;
        } else if (m.otherId == this.otherId) {
            return true;
        } else {
            return false;
        }
    } 
    return false;
}

public boolean equals(Long l) {
    MyClass m = new MyClass();
    m.setOtherId(l);
    return equals(m);
}
Eran
  • 387,369
  • 54
  • 702
  • 768
  • I believe your answer is technically the correct answer to my questions and the way to go. I accepted Tagir's answer because that is what I'm now using. – Jochen Feb 29 '16 at 10:41