简体   繁体   中英

searching keys of subclass in HashMap

just tried to do something like:

public class GameMap {

protected HashMap<Sector, Integer[]> mapping;

protected void loadMapFromFile(String fileLocation, int numPlayers) {

    .
    //Other stuff
    .
    .

    case "ALIENSECT":

    Integer[] someValue = {5};
    mapping.put(new AlienSector(row, col), someValue);
    break;
}

public justATestMethod() {

    System.out.println(mapping.containsKey(new Sector(6, 'L')));
}

Where AlienSector is a subclass of Sector .

But when I try to do this in another class:

   mappa.justATestMethod();

The result is "false".

Instead if I rewrite the method "justATestMethod()" like this:

System.out.println(mapping.containsKey(new AlienSector(6, 'L')));

Result is "true".

I obtain "true" also changing this lines of "loadMapFromFile" method:

case "ALIENSECT":

Integer[] someValue = {5};
mapping.put(new AlienSector(row, col), someValue);
break;

This way:

case "ALIENSECT":

mapping.put(new Sector(row, col), new Integer[1]);
Integer[] aCazzo = {5};
mapping.put(new AlienSector(row, col), aCazzo);
break;

That is first filling the HashMap with Sector objects keys and then assigning keys of AlienSector objects.

Someone could explain me why this happens? AlienSector is a subclass of Sector , why Java doesn't recognize the presence of a Sector in the HashMap keys if I simply instantiate a subclass of it without first instantiate the key with an istance of the superclass "Sector" itself?

You are storing an AlienSector in the HashMap, and then trying to retrieve it with another Sector created using the same parameters. When you try to retrieve an object from a HashMap it is looking for an object that is 'equal' to the one you stored . But by default Java does not recognize two objects as 'equal' just because they have the same members. By default they are only equal if they are the same object (Strings, Integers etc. are special cases).

What you need to do is tell Java that two objects with the same parameters are 'equal' by overriding the 'equals' method . From your test results it looks like yo uhave done this for AlienSector. But you will need to do this for both Sector and AlienSector, and arrange it so the objects are considered equal even if they have different classes ie an AlienSector is considered equal to a Sector with the same members, and a Sector is considered equal to an AlienSector with the same members. There are tutorials on how to do this.

You will also need to override the hashCode() method to make sure that any two objects that would be considered 'equal' also return the same hashCode. HashMap uses hashCode a filter, deciding that things with different hashCodes can never be equal.

The details of all this are too long to put in an answer like this.

by the way, if you used the same object in the containsKey call, instead of creating a new one, you would find it worked.

You can use the class below to perform this behaviour.

One caveat to note is that if it is a large map, it may not be particularly performant, but for most cases the size of the map will be small so there is no real performance impact with this.

NOTE: JDK8+ code

Essentially we override the regular hashmap class methods for containsKey and get to do the appropriate searching.

import java.util.HashMap;
import java.util.Optional;

public class PolymorphicHashMap<K extends Class<?>,V> extends HashMap<K,V> {

  @Override
  public boolean containsKey(Object key) {
    return findEntry((K)key).isPresent();
  }

  @Override
  public V get(Object key) {
    var entry = findEntry((K)key);
    return entry.map(Entry::getValue).orElse(null);
  }

  private Optional<Entry<K,V>> findEntry(K key) {
    return entrySet().stream()
        .filter(e -> e.getKey().isAssignableFrom(key))
        .findFirst();
  }

}

HashMap uses the hashCode() function in order to lookup and store key/value pairs.
I believe you need to have both the superclass/subclass return the same hashcode in order to be able to lookup the keys of subclass in the HashMap.

public class AlienSector {
    public int hashcode() {
        //
        // Generate a hashcode unique to this AlienSector object here
        //
    }
}

public class Sector {
    public int hashCode() {
        return super.hashCode(); // Return the same hashcode as the super class
    }
}

As pointed out in the comment, the rule is that if you override the hashCode() function, you need to override the equals() function as well (and vice-versa)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM