0

I have a method that is expected to save an object in a hashmap (used as a cache) that has as a key a String.
The objects that are passed in the method have either fields that are “volatile” i.e. they can change on a next refresh from the data store or are the same across all objects except for 3 fields.
Those fields are 2 of type double and 1 field of type String.
What I am doing now is I use:

Objects.hash(doubleField1, doubleField2, theString)   

and convert the result to String.

Basically I am generating a key for that object based on the immutable state.

This seems to work but I have the following question:

If we exclude the case that we have 2 objects with the exact same fields (which is impossible) how likely is that I could end up with a collision that won’t be able to be verified properly?
I mean that if I have a hashmap with keys e.g. strings etc if there is a collision on the hashCode the actual value of the key is compared to verify if it is the same object and not a collision.
Would using keys the way I have described create problems in such verification?

Update:
If I have e.g. a hashmap with key a Person and the hashCode is generated using fullName and ssn or dateOfBirth if there is a collision then the hashmap implementation uses equals to verify if it is the actual object being searched for.
I was wondering if the approach I describe could have some issue in that part because I generate the actual key directly

Jim
  • 3,845
  • 3
  • 22
  • 47
  • If you are talking about a key where the hashCode changes, then that is a bad idea. Keys should be immutable. If you are talking about collisions, then I don't think that should be a problem. A hashCode implementation could return a constant like 42. But then your map would not be very efficient as it would degenerate into a linked list. But I am not totally sure what you mean by verification. – WJS May 25 '21 at 14:36
  • `HashMap` (as well as `HashSet`) Identify objects not only via `hashcode()` but also via `equals()` Therefore it is important to fulfill the [Hashcode/Equals contract](https://www.baeldung.com/java-equals-hashcode-contracts). – Timothy Truckle May 25 '21 at 14:39
  • @WJS: 1) The keys won't change. 2) If I have e.g. a hashmap with key a `Person` and the `hashCode` is generated using `fullName` and `ssn` or `dateOfBirth` if there is a collision then the hashmap implementation uses `equals` to verify if it is the actual object being searched for. I was wondering if the approach I describe could have some issue in that part – Jim May 25 '21 at 14:39
  • @TimothyTruckle: I save as a `String` so that should not be an issue – Jim May 25 '21 at 14:39
  • As far as I red you create an Hash an use this as the Key. That **will** have collisions. – Timothy Truckle May 25 '21 at 14:41
  • @TimothyTruckle: I understand it can happen. My question is about if it could create some problem with how the hashmap verifies that a collision happened. If I have e.g. a hashmap with key a Person and the hashCode is generated using fullName and ssn or dateOfBirth if there is a collision then the hashmap implementation uses equals to verify if it is the actual object being searched for. I was wondering if the approach I describe could have some issue in that part – Jim May 25 '21 at 14:43
  • The hashCode only gets you to the correct bucket. Each bucket has a unique pairing of keys to values. As you go thru the bucket you look for the key (via equals) and then return the object associated with it. I do not see how anything you are doing could affect that behavior as long as your keys are immutable. You can always change the object associated with a key. – WJS May 25 '21 at 14:46
  • @WJS: I was thinking the following scenario: 2 different objects with different values in the fields, still generate somehow the same hashcode e.g. due to overflow or something and this subsequently is used as a key in the hashmap (after being converted to string). Does this make any sense to happen? – Jim May 25 '21 at 14:48
  • Even a bad hashcode will not affect the proper workings of a HashMap. It may not be efficient but you will always be able to look up the value. The values play no role in storing or retrieving themselves. Only the keys. – WJS May 25 '21 at 14:52
  • @WJS: Just to be sure, I understand you properly. For `Person(fistName, dateOfBirth)` the `hashCode` is generated on those 2 fields. The hashmap implementation on verifying that it has the right key, it also calls equals on the those fields. So you are saying my approach does not have any implications on that right? – Jim May 25 '21 at 14:54
  • 1
    If you use the hash as key in the `HashMap` and a collision occurs the first objects stored in the `HashMap` using this same key will be lost. – Timothy Truckle May 25 '21 at 14:56
  • Yes. You can verify this by storing some objects under certain keys and then change those objects. When you retrieve them using the proper key, they will have been changed but you will still retrieve them. But remember, that keys must be unique. Using the same key to store a subsequent value will replace the existing one. – WJS May 25 '21 at 14:58
  • @Jim one thing you have to know is that the HashMap can't call equals on the fields, since they are hidden away in your class. It calls the equals method on your object, so there you should implement the equals logic in a way that fits your needs. – McModknower May 25 '21 at 14:58
  • @TimothyTruckle: Got you. So I could just convert the actual values to string and then let it become a hashcode. That should be safe right? Similar to what scripting languages do – Jim May 25 '21 at 15:05
  • 1
    Yes. That is an option. – Timothy Truckle May 25 '21 at 15:08

1 Answers1

0

Here is a simple demo for a hashMap key implementation. When retrieving the object I construct the fields piecemeal to avoid any possibility of using cached Strings or Integers. It makes a more convincing demo.

Map<MyKey, Long> map = new HashMap<>();
map.put(new MyKey(10,"abc"), 1234556L);
map.put(new MyKey(400,"aefbc"), 548282L);

int n = 380;
long v = map.get(new MyKey(n + 20, "ae" + "fbc")); // Should get 548282
System.out.println(v);

prints

548282

The key class

class MyKey {
    privat eint v;
    private String s;
    private int hashcode;
    
    public MyKey(int v, String s) {
        Objects.requireNonNull(s, "String must be provided");
        this.v = v;
        this.s = s;
        // this class is immutable so no need to keep 
        // computing hashCode
        hashcode = Objects.hash(s,v);
    }
    @Override
    public int hashCode() {
        return hashcode;
    }
    
    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (o == null) {
            return false;
        }
        if (o instanceof MyKey) {
            MyKey mk = (MyKey)o;
            return v == mk.v && s.equals(mk.s);
        }
        return false;
    }
}
WJS
  • 36,363
  • 4
  • 24
  • 39