Reputation: 23
This sounds like a really weird request but I have a HashMap
of a custom class. I've overridden the equals
and hashCode
methods to only focus on certain fields, so that I can pull a key if it equals a new key with the same certain fields. In that case, I want to replace the other fields with some new values. The structure is like so:
public class ExampleClass() {
int field1;
int field2;
<insert constructor here with field1 and field2>
@Override
public boolean equals(Object obj) { // Only return true if field1 is equal
...
return (this.field1 == obj.field1);
}
}
So I use it like this:
HashMap<ExampleClass, int> hmap = new HashMap<>();
while(true) {
...
ExampleClass oldObject = new ExampleClass(1, 2);
ExampleClass newObject = new ExampleClass(1, 5);
hmap.put(oldObject, 10);
if(hmap.contains(newObject)) {
// Get field1 of old object and change it
}
}
This was a bad example but I just want to be able to retrieve the key object of a key-value pair in a HashMap
given that I have the key so that I can modify the key. How would I do so?
Edit: My hashcode function.
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result +
((this.srcVertex.getVertexData().getID() == null) ? 0 : this.srcVertex.getVertexData().getID().hashCode());
result = prime * result +
((this.targetVertex.getVertexData().getID() == null) ? 0 : this.targetVertex.getVertexData().getID().hashCode());
return result;
}
Upvotes: 0
Views: 59
Reputation: 51037
As I understand your question, you have a key object in the HashMap, and you want to use an "equal" key object to retrieve the key rather than the value it's associated with. There is no method on a HashMap to do that, and it somewhat violates the idea of two objects being "equal" if you do actually care which of the two equal objects you get.
I think it would make more sense to do this in a different way:
ExampleKey
with just the fields that you want to use in the equals
/hashCode
methods for the purposes of the HashMap. This class must override equals
and hashCode
using those fields, and it should be immutable (the behaviour is undefined if a key's hash can change while it's in the HashMap).ExampleClass
a getKey()
method which returns an ExampleKey
object for the current object. It is probably simpler to use composition here, so that ExampleClass
doesn't duplicate those fields.Map<ExampleKey, Integer>
for the actual mapping that you want to store, and a separate Map<ExampleKey, ExampleClass>
storing the object which would otherwise have been used as the key in the other HashMap.Example usage:
Map<ExampleKey, Integer> actualMapping = new HashMap<>();
Map<ExampleKey, ExampleClass> objsUsed = new HashMap<>();
while(true) {
// ...
ExampleClass oldObject = new ExampleClass(1, 2);
ExampleClass newObject = new ExampleClass(1, 5);
// always update both maps together, to ensure valid state
actualMapping.put(oldObject.getKey(), 10);
objsUsed.put(oldObject.getKey(), oldObject);
// ...
ExampleClass objUsed = objsUsed.get(newObject.getKey());
if(objUsed != null) {
// objUsed == oldObject here
}
}
If you don't care about the philosophy of what "equal" is supposed to mean, then you can apply this same solution without the ExampleKey
class or the getKey
method; just use the objects themselves, i.e. objsUsed
would be of type Map<ExampleClass, ExampleClass>
and it would always map an object to itself. But I think if you do that, readers of your code will be scratching their heads wondering why you are mapping objects to themselves.
Upvotes: 1
Reputation: 18027
You forgot to define the hashCode() method. Without it, storing an object as a key in a HashMap doesn't work.
UPDATE:
If srcVertex is field1 and targetVertex is field2, then your hashCode() method is incorrect. If equals() compares srcVertex, then hashCode() should use only srcVertex, not targetVertex.
The rule is: if 2 objects are equal, then their hash codes must be equal.
Upvotes: 0
Reputation: 147124
Map
s in Java should be keyed on values that have equals
defined over all their 'essential properties'. I believe most collection libraries work like this, with the only example that springs to mind is that of General Magic's Telescript.
So, have a Map
defined on a type of only those properties. The field1
int
(Integer
) in this case. Put the rest of the information in the map entry value. This may well be a new class.
Map<Integer, ValueClass> map;
where
public final class ValueClass {
private int someValue;
private ExampleClass exmaple;
...
If you are insistent you want to find the key, which I suggest you don't. There's various ways of doing it, something like:
Optional<ExampleClass> found = map.keySet().stream()
.firstThat(k -> k.field1() == target);
found.ifPresent(key -> {
Integer value = hmap.remove(key);
// update key.
hmap.put(key, value);
});
Or the old school version (looks better to me, but not so cool):
for (ExampleClass key : map.keySet()) {
if (key.field1() == target) {
Integer value = hmap.remove(key);
// update key.
hmap.put(key, value);
}
}
Using an Iterator
or possibly over an ihe entry set is better in that it avoids the second of three lookups, but I'll leave that as an exercise.
Upvotes: 0