web-dev-qa-db-fra.com

ArrayList comme clé dans HashMap

Serait-il possible d'ajouter un ArrayList comme clé de HashMap. Je voudrais conserver le nombre de fréquences des bigrammes. Le bigramme est la clé et la valeur est sa fréquence.

Pour chacun des bigrammes comme "il est", je crée un ArrayList pour lui et l'insère dans le HashMap. Mais je n'obtiens pas la sortie correcte.

public HashMap<ArrayList<String>, Integer> getBigramMap(String Word1, String Word2) {
    HashMap<ArrayList<String>, Integer> hm = new HashMap<ArrayList<String>, Integer>();
    ArrayList<String> arrList1 = new ArrayList<String>();
    arrList1 = getBigram(Word1, Word2);
    if (hm.get(arrList1) != null) {
        hm.put(arrList1, hm.get(arrList1) + 1);
    } else {
        hm.put(arrList1, 1);
    }
    System.out.println(hm.get(arrList1));
    return hm;
}


public ArrayList<String> getBigram(String Word1, String Word2) {
    ArrayList<String> arrList2 = new ArrayList<String>();
    arrList2.add(Word1);
    arrList2.add(Word2);
    return arrList2;
}
19
thetna

Oui, vous pouvez avoir ArrayLists comme clés dans une carte de hachage, mais c'est un très mauvaise idée car ils sont mutables.

Si vous modifiez le ArrayList de quelque façon que ce soit (ou l'un de ses éléments), le mappage sera fondamentalement perdu, car la clé n'aura pas le même hashCode que lors de son insertion .

La règle d'or consiste à n'utiliser que des types de données immuables comme clés dans une carte de hachage. Comme suggéré par Alex Stybaev, vous souhaiterez probablement créer une classe Bigram comme ceci:

final class Bigram {

    private final String Word1, Word2;

    public Bigram(String Word1, String Word2) {
        this.Word1 = Word1;
        this.Word2 = Word2;
    }

    public String getWord1() {
        return Word1;
    }

    public String getWord2() {
        return Word2;
    }

    @Override
    public int hashCode() {
        return Word1.hashCode() ^ Word2.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return (obj instanceof Bigram) && ((Bigram) obj).Word1.equals(Word1)
                                       && ((Bigram) obj).Word2.equals(Word2);
    }
}
26
aioobe

De la documentation :

Remarque: une grande prudence doit être exercée si des objets mutables sont utilisés comme clés de carte. Le comportement d'une carte n'est pas spécifié si la valeur d'un objet est modifiée d'une manière qui affecte les comparaisons equals alors que l'objet est une clé de la carte. Un cas particulier de cette interdiction est qu'il n'est pas permis à une carte de se contenir comme clé. S'il est permis à une carte de se contenir en tant que valeur, une extrême prudence est conseillée: les méthodes equals et hashCode ne sont plus bien définies sur une telle carte.

Vous devez faire attention lorsque vous utilisez des objets mutables comme clés pour le bien de hashCode et equals.

L'essentiel est qu'il est préférable d'utiliser des objets immuables comme clés.

2

Pourquoi ne pouvez-vous pas utiliser quelque chose comme ça:

class Bigram{
    private String firstItem;
    private String secondItem;

    <getters/setters>

    @Override
    public int hashCode(){
        ...
    }

    @Override 
    public boolean equals(){
        ...
    }
}

au lieu d'utiliser la collection dynamique pour un nombre limité d'éléments (deux).

2
Alex Stybaev

J'ai trouvé cette solution. Il n'est évidemment pas utilisable dans tous les cas, par exemple en cas de dépassement de la capacité int de hashcodes, ou de complications list.clone () (si la liste d'entrée est modifiée, la clé reste la même que prévu, mais lorsque les éléments de List sont mutables, clonés liste a la même référence à ses éléments, ce qui entraînerait la modification de la clé elle-même).

import Java.util.ArrayList;

public class ListKey<T> {
    private ArrayList<T> list;

    public ListKey(ArrayList<T> list) {
        this.list = (ArrayList<T>) list.clone();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;

        for (int i = 0; i < this.list.size(); i++) {
            T item = this.list.get(i);
            result = prime * result + ((item == null) ? 0 : item.hashCode());
        }
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        return this.list.equals(obj);
    }
}

---------
    public static void main(String[] args) {

        ArrayList<Float> createFloatList = createFloatList();
        ArrayList<Float> createFloatList2 = createFloatList();

        Hashtable<ListKey<Float>, String> table = new Hashtable<>();
        table.put(new ListKey(createFloatList2), "IT WORKS!");
        System.out.println(table.get(createFloatList2));
        createFloatList2.add(1f);
        System.out.println(table.get(createFloatList2));
        createFloatList2.remove(3);
        System.out.println(table.get(createFloatList2));
    }

    public static ArrayList<Float> createFloatList() {
        ArrayList<Float> floatee = new ArrayList<>();
        floatee.add(34.234f);
        floatee.add(new Float(33));
        floatee.add(null);

        return floatee;
    }

Output:
IT WORKS!
null
IT WORKS!
1
Javo

Essayez ceci, cela fonctionnera.

 public Map<List, Integer> getBigramMap (String Word1,String Word2){
    Map<List,Integer> hm = new HashMap<List, Integer>();
    List<String> arrList1 = new ArrayList<String>();
    arrList1 = getBigram(Word1, Word2);     
    if(hm.get(arrList1) !=null){
        hm.put(arrList1, hm.get(arrList1)+1);
    }
    else {
        hm.put(arrList1, 1);
    }

    System.out.println(hm.get(arrList1));
    return hm;
}
1
vikiiii

Bien sûr, c'est possible. Je suppose que le problème dans votre put. Essayez d'obtenir la clé pour le bigramme, incrémentez-la, supprimez l'entrée avec ce bigramme et insérez la valeur mise à jour

0
mishadoff