web-dev-qa-db-fra.com

Comment faire en sorte que HashMap fonctionne avec des tableaux comme clé?

J'utilise des tableaux booléens comme clés d'un HashMap. Mais le problème est que HashMap ne parvient pas à obtenir les clés lorsqu'un tableau différent est passé en tant que clé, bien que les éléments soient les mêmes. (Comme ce sont des objets différents).

Comment puis-je le faire fonctionner avec des tableaux comme des clés? Voici le code:

public class main {
public static HashMap<boolean[], Integer> h;


public static void main(String[] args){
    boolean[] a = {false, false};

    h = new HashMap<boolean[], Integer>();
    h.put(a, 1);


    if(h.containsKey(a)) System.out.println("Found a");

    boolean[] t = {false, false};

    if(h.containsKey(t)) System.out.println("Found t");
    else System.out.println("Couldn't find t");

}

}

Les tableaux a et t contiennent les mêmes éléments, mais HashMap ne renvoie rien pour t.

Comment puis-je le faire fonctionner?

16
gaganbm

Vous ne pouvez pas le faire de cette façon. t et a auront des valeurs hashCode() différentes, car la méthode Java.lang.Array.hashCode() est héritée de Object, qui utilise la référence pour calculer le code de hachage (implémentation par défaut). Par conséquent, le code de hachage pour les tableaux dépend de la référence, ce qui signifie que vous obtiendrez une valeur de code de hachage différente pour t et a. De plus, equals ne fonctionnera pas pour les deux tableaux, car il est également basé sur la référence.

La seule façon de procéder consiste à créer une classe personnalisée qui conserve le tableau boolean en tant que membre interne. Vous devez ensuite redéfinir equals et hashCode de manière à ce que les instances contenant des tableaux avec des valeurs identiques soient égales et possèdent le même code de hachage.

Une option plus simple pourrait être d'utiliser List<Boolean> comme clé. Selon la documentation l'implémentation hashCode() pour List est définie comme suit:

int hashCode = 1;
Iterator<E> i = list.iterator();
while (i.hasNext()) {
    E obj = i.next();
    hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}

Comme vous pouvez le constater, cela dépend des valeurs de votre liste et non de la référence. Cela devrait donc fonctionner pour vous.

22
Vivin Paliath

Cela n'est pas possible avec les tableaux, car deux tableaux différents ne comparent pas equals, même s'ils ont les mêmes éléments.

Vous devez mapper à partir de la classe conteneur, par exemple ArrayList<Boolean> (ou simplement List<Boolean>. Peut-être que BitSet serait encore plus approprié.

8
zch

Les implémentations Map reposent sur les méthodes equals et hashCode de la clé. Les tableaux en Java sont directement étendus à partir de Object, ils utilisent les valeurs par défaut equals et hashCode de Object qui ne compare que identity.

Si j'étais vous, je créerais une classe Key

class Key {
    private final boolean flag1;
    private final boolean flag2;

    public Key(boolean flag1, boolean flag2) {
        this.flag1 = flag1;
        this.flag2 = flag2;
    }

    @Override
    public boolean equals(Object object) {
        if (!(object instanceof Key)) {
            return false;
        }

        Key otherKey = (Key) object;
        return this.flag1 == otherKey.flag1 && this.flag2 == otherKey.flag2;
    }

    @Override
    public int hashCode() {
        int result = 17; // any prime number
        result = 31 * result + Boolean.valueOf(this.flag1).hashCode();
        result = 31 * result + Boolean.valueOf(this.flag2).hashCode();
        return result;
    }
}

Après cela, vous pouvez utiliser votre clé avec Map:

Map<Key, Integer> map = new HashMap<>();

Key firstKey = new Key(false, false);
map.put(firstKey, 1);

Key secondKey = new Key(false, false) // same key, different instance
int result = map.get(secondKey); // --> result will be 1

Référence: Code de hachage Java d'un champ

4
Genzer
boolean[] t;
t = a;

Si vous donnez ceci, au lieu de boolean[] t = {false, false};, vous obtiendrez le résultat souhaité.

En effet, Map stocke reference en tant que key et, dans votre cas, même si t a les mêmes valeurs, il n'a pas la même référence que a.

Par conséquent, lorsque vous donnez t=a, cela fonctionnera.

C'est très semblable à ceci: -

String a = "ab";
String b = new String("ab");

System.out.println(a==b); // This will give false.

a & b ont la même valeur, mais ont des références différentes. Par conséquent, lorsque vous essayez de comparer la référence en utilisant ==, il donne false.

Mais si vous donnez a = b; puis essayez de comparer reference, vous obtiendrez true.

1
SudoRahul

Vous pouvez créer une classe contenant le tableau. Implémente les méthodes hashCode () et equals () pour cette classe, en fonction de valeurs:

public class boolarray {
  boolean array[];

  public boolarray( boolean b[] ) {
     array = b;
  }

  public int hashCode() {
    int hash = 0;
    for (int i = 0; i < array.length; i++)
       if (array[i])
          hash += Math.pow(2, i);
    return hash;
  }

  public boolean equals( Object b ) {
     if (!(b instanceof boolarray))
        return false;
     if ( array.length != ((boolarray)b).array.length )
        return false;
     for (int i = 0; i < array.length; i++ )
        if (array[i] != ((boolarray)b).array[i])
           return false;
     return true;
  }
}

Vous pouvez ensuite utiliser:

 boolarray a = new boolarray( new boolean[]{ true, true } );
 boolarray b = new boolarray( new boolean[]{ true, true } );
 HashMap<boolarray, Integer> map = new HashMap<boolarray, Integer>();
 map.put(a, 2);
 int c = map.get(b);
 System.out.println(c);
1
Jeroen Vuurens

C’est probablement parce que la méthode equals () pour les retours sur les tableaux est différente de celle que vous attendez. Vous devriez penser à mettre en œuvre votre propre collecte et surcharger equals () et hashCode ().

1
FazoM

Map utilise equals() pour vérifier si vos clés sont identiques.

L'implémentation par défaut de cette méthode dans Object teste ==, c'est-à-dire une égalité de référence. Donc, comme vos deux tableaux ne sont pas identiques, equals renvoie toujours false.

Pour vérifier l’égalité, vous devez appeler le mappage Arrays.equals sur les deux tableaux.

Vous pouvez créer une classe wrapper de tableau qui utilise Arrays.equals et cela fonctionnera comme prévu:

public static final class ArrayHolder<T> {

    private final T[] t;

    public ArrayHolder(T[] t) {
        this.t = t;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 23 * hash + Arrays.hashCode(this.t);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final ArrayHolder<T> other = (ArrayHolder<T>) obj;
        if (!Arrays.equals(this.t, other.t)) {
            return false;
        }
        return true;
    }
}

public static void main(String[] args) {
    final Map<ArrayHolder<Boolean>, Integer> myMap = new HashMap<>();

    myMap.put(new ArrayHolder<>(new Boolean[]{true, true}), 7);
    System.out.println(myMap.get(new ArrayHolder<>(new Boolean[]{true, true})));
}
1
Boris the Spider

Cela devrait fonctionner pour les tableaux de tout type:

class ArrayHolder<T> {
    private final T[] array;
    @SafeVarargs
    ArrayHolder(T... ts) { array = ts; }
    @Override public int hashCode() { return Arrays.hashCode(array); }
    @Override public boolean equals(Object other) {
        if (array == other) { return true; }
        if (! (other instanceof ArrayHolder) ) {
            return false;
        }
        //noinspection unchecked
        return Arrays.equals(array, ((ArrayHolder) other).array);
    }
}

Voici votre exemple spécifique converti pour utiliser ArrayHolder:

// boolean[] a = {false, false};
ArrayHolder<Boolean> a = new ArrayHolder<>(false, false);

// h = new HashMap<boolean[], Integer>();
Map<ArrayHolder<Boolean>, Integer> h = new HashMap<>();

h.put(a, 1);

// if(h.containsKey(a)) System.out.println("Found a");
assertTrue(h.containsKey(a));

// boolean[] t = {false, false};
ArrayHolder<Boolean> t = new ArrayHolder<>(false, false);

// if(h.containsKey(t)) System.out.println("Found t");
assertTrue(h.containsKey(t));

assertFalse(h.containsKey(new ArrayHolder<>(true, false)));

J'ai utilisé Java 8, mais je pense que Java 7 a tout ce dont vous avez besoin pour cela. J'ai testé hashCode et égal à l'aide de TestUtils .

Une autre pensée est celle de l’article 25 de Joshua Bloch: "Préférez les listes aux tableaux".

0
GlenPeterson

Vous pouvez utiliser une bibliothèque qui accepte une stratégie de hachage et de comparaison externe (outil).

class MyHashingStrategy implements HashingStrategy<boolean[]> {

    @Override
    public int computeHashCode(boolean[] pTableau) {
        return Arrays.hashCode(pTableau);
    }

    @Override
    public boolean equals(boolean[] o1, boolean[] o2) {
        return Arrays.equals(o1, o2);
    }
}


Map<boolean[], T> map = new TCustomHashMap<boolean[],T>(new MyHashingStrategy());
0
cquezel