Подтвердить что ты не робот

ArrayList как ключ в Hashmap

Можно ли добавить ArrayList в качестве ключа HashMap. Я хотел бы сохранить частоту числа биграмм. Bigram - это ключ, а значение - его частота.

Для каждого из таких биграмм, как "он есть", я создаю для него ArrayList и вставляю его в HashMap. Но я не получаю правильный вывод.

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;
}
4b9b3361

Ответ 1

Да, вы можете иметь ArrayList как ключи в хэш-карте, но это очень плохая идея, поскольку они изменяемы.

Если вы измените ArrayList каким-либо образом (или любым из его элементов), сопоставление будет в основном потеряно, так как ключ не будет иметь тот же hashCode, какой он был, когда он был вставлен.

Эмпирическое правило состоит в том, чтобы использовать только неизменные типы данных в качестве ключей в карте хэша. Как предложил Алекс Стибаев, вы, вероятно, захотите создать класс Bigram следующим образом:

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);
    }
}

Ответ 2

Почему вы не можете использовать что-то вроде этого:

class Bigram{
    private String firstItem;
    private String secondItem;

    <getters/setters>

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

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

вместо использования динамической коллекции для ограниченного количества элементов (два).

Ответ 3

Из документация:

Примечание: следует проявлять большую осторожность, если изменяемые объекты используются в качестве карты      ключи. Поведение карты не указывается, если значение объекта      изменен таким образом, который влияет на сравнение equals, в то время как      объект - это ключ на карте. Частным случаем этого запрета является то, что оно      недопустимо, чтобы карта содержала себя как ключ. Хотя это      допустимо, чтобы карта содержала себя как значение, с особой осторожностью      рекомендуется: методы equals и hashCode больше не используются      хорошо определенная на такой карте.

Вам нужно позаботиться, когда вы используете изменяемые объекты в качестве ключей для hashCode и equals.

Суть в том, что лучше использовать неизменяемые объекты в качестве ключей.

Ответ 4

Попробуйте, это сработает.

 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;
}

Ответ 5

Я придумал это решение. Очевидно, что он не может использоваться во всех случаях, например, при переходе на уровень hashcodes int capacity или list.clone() (при изменении списка ввода ключ остается таким же, как и предполагалось, но когда элементы List изменяются, клонируются список имеет одинаковую ссылку на его элементы, что приведет к изменению самого ключа).

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!

Ответ 6

Конечно, это возможно. Я предполагаю, что проблема в вашем put. Попробуйте получить ключ для bigram, увеличьте его, удалите запись с помощью этого bigram и вставьте обновленное значение

Ответ 7

Пожалуйста, проверьте ниже мой код, чтобы понять, является ли ключ ArrayList в Map и как JVM сделает это для ввода: здесь я пишу метод hashCode и equals для класса TesthashCodeEquals.

package com.msq;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class TesthashCodeEquals {
    private int a;
    private int b;

    public TesthashCodeEquals() {
        // TODO Auto-generated constructor stub
    }



    public TesthashCodeEquals(int a, int b) {
        super();
        this.a = a;
        this.b = b;
    }



    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }

    public int hashCode() {

        return this.a + this.b;
    }

    public boolean equals(Object o) {

        if (o instanceof TesthashCodeEquals && o != null) {

            TesthashCodeEquals c = (TesthashCodeEquals) o;

            return ((this.a == c.a) && (this.b == c.b));

        } else
            return false;
    }
}

public class HasCodeEquals {
    public static void main(String[] args) {

        Map<List<TesthashCodeEquals>, String> m = new HashMap<>();

        List<TesthashCodeEquals> list1=new ArrayList<>();
        list1.add(new TesthashCodeEquals(1, 2));
        list1.add(new TesthashCodeEquals(3, 4));

        List<TesthashCodeEquals> list2=new ArrayList<>();
        list2.add(new TesthashCodeEquals(10, 20));
        list2.add(new TesthashCodeEquals(30, 40));


        List<TesthashCodeEquals> list3=new ArrayList<>();
        list3.add(new TesthashCodeEquals(1, 2));
        list3.add(new TesthashCodeEquals(3, 4));



        m.put(list1, "List1");
        m.put(list2, "List2");
        m.put(list3, "List3");

        for(Map.Entry<List<TesthashCodeEquals>,String> entry:m.entrySet()){
            for(TesthashCodeEquals t:entry.getKey()){
                System.out.print("value of a: "+t.getA()+", value of b: "+t.getB()+", map value is:"+entry.getValue() );
                System.out.println();
            }
            System.out.println("######################");
        }

    }
}

.

output:

value of a: 10, value of b: 20, map value is:List2
value of a: 30, value of b: 40, map value is:List2
######################
value of a: 1, value of b: 2, map value is:List3
value of a: 3, value of b: 4, map value is:List3
######################

поэтому это проверит количество объектов в списке и значения valriabe в объекте. если количество объектов одинаково, а значения переменных экземпляра тоже одинаковы, тогда он рассмотрит дубликат ключа и переопределит ключ.

теперь, если я изменяю только значение объекта в списке3

list3.add(новый TesthashCodeEquals (2, 2));

то он будет печатать:

 output
    value of a: 2, value of b: 2, map value is:List3
    value of a: 3, value of b: 4, map value is:List3
    ######################
    value of a: 10, value of b: 20, map value is:List2
    value of a: 30, value of b: 40, map value is:List2
    ######################
    value of a: 1, value of b: 2, map value is:List1
    value of a: 3, value of b: 4, map value is:List1
######################

чтобы всегда проверять количество объектов в списке и значение переменной экземпляра объекта.

спасибо

Ответ 8

ArrayList.equals() наследуется от java.lang.Object - поэтому equals() в ArrayList не зависит от содержимого списка.

Если вы хотите использовать ArrayList в качестве ключа карты, вам нужно будет переопределить equals() и hashcode(), чтобы два arraylists с одним и тем же содержимым в том же порядке возвращали true при вызове equals() и вернуть тот же хэш-код при вызове hashcode().

Есть ли какая-то конкретная причина, по которой вам нужно использовать ArrayList, а не просто строку String?

edit: Игнорируйте меня, как указал Йоахим Зауэр ниже, я настолько ошибаюсь, что это даже не смешно.