简体   繁体   English

Java HashMap / Hashtable不会检索自己的密钥

[英]Java HashMap/Hashtable does not retrieve its own keys

For a school project, I am currently coding a program which learns how to play TicTacToe thanks to a Q-Learning algorithm, based on the following sources : https://github.com/aimacode/aima-java/blob/AIMA3e/aima-core/src/main/java/aima/core/learning/reinforcement/agent/QLearningAgent.java 对于一个学校项目,我目前正在编写一个程序,该程序基于以下来源,借助Q学习算法来学习如何玩TicTacToe: https : //github.com/aimacode/aima-java/blob/AIMA3e/aima -core / SRC /主/爪哇/ AIMA /型芯/学习/加固/剂/ QLearningAgent.java

But I identified a more specific problem, and in spite of all my researches and trials to debug it, I can't solve it. 但是我发现了一个更具体的问题,尽管我进行了很多研究和尝试来调试它,但还是无法解决。

The algorithm uses a HashMap to store Q-Values, here is its definition : 该算法使用HashMap来存储Q值,这是其定义:

Map<Pair<S, A>, Double> Q = new Hashtable<Pair<S, A>, Double>();

Then, I implemented my classes such that S is a Grid, and A is a DynamicAction, and I concretely use instances of TicTacToeAction and NoOpAction, which both extends DynamicAction. 然后,我实现了类,以使S为Grid,A为DynamicAction,并具体使用TicTacToeAction和NoOpAction实例,它们都扩展了DynamicAction。

Here are hashCode and equals for Grid : 这里是hashCode,等于Grid:

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((matrix == null) ? 0 : matrix.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Grid other = (Grid) obj;
    if (matrix == null) {
        if (other.matrix != null)
            return false;
    } else {
        System.out.println("this : " + this);
        System.out.println("other : " + other);
        if (!matrix.equals(other.matrix)) {
            return false;
        }
    }
    System.out.println("this and other are equals");
    return true;
}

With matrix being defined like this : 像这样定义矩阵:

private ArrayList<ArrayList<String>> matrix = new ArrayList<ArrayList<String>>();

For TicTacToeAction it is a bit more complicated, because it extends DynamicAction, which itself extends a class ObjectWithDynamicAttributes, which implements hashCode and equals, but those part of the code come from the GitHub at the previous link. 对于TicTacToeAction,它有点复杂,因为它扩展了DynamicAction,后者本身扩展了一个类ObjectWithDynamicAttributes,该类实现了hashCode和equals,但是代码的这些部分来自GitHub的上一个链接。

For Pair, hashCode and equals are simply defined like this : 对于Pair,hashCode和equals的定义如下:

@Override
public boolean equals(Object o) {
    if (o instanceof Pair<?, ?>) {
        Pair<?, ?> p = (Pair<?, ?>) o;
        return a.equals(p.a) && b.equals(p.b);
    }
    return false;
}

@Override
public int hashCode() {
    return a.hashCode() + 31 * b.hashCode();
}

But I have some huge problems when the program try to use put and get methods from the QLearningAgent class. 但是,当程序尝试使用QLearningAgent类中的putget方法时,我遇到了一些巨大的问题。 It appends that it seldom retrieves previously entered values, and worse, the keySet() seems to contain duplicate elements! 它补充说它很少检索以前输入的值,更糟糕的是,keySet()似乎包含重复的元素! Moreover, thanks to prints in equals methods, I saw that it is a bit like if the put and get methods did not check the entire KeySet to see if the entry exists, so the same entries are put again and again... However, when I try to put values by hand in a similar HashMap, defined with the same classes, such problems don't seem to happend. 此外,多亏了equals方法中的prints,我看到有点像putget方法没有检查整个KeySet来查看条目是否存在,因此一次又一次地放置相同的条目...但是,当我尝试将值手动放入使用相同类定义的类似HashMap中时,似乎不会发生此类问题。

Maybe the most ununderstandable example is this one : 也许最不可理解的例子是这个例子:

This part of code : 这部分代码:

    for(Pair<S, A> pair : Q.keySet()) {
        System.out.println(pair);
        System.out.println("Contains? " + Q.keySet().contains(pair));
        System.out.println("value : " + Q.get(pair));
    }

Returns this (among other things...) : 返回这个(除其他外...):

< -------------
| 1 | O | X |
-------------
| 4 | X | O |
-------------
| X | O | X |
-------------
 , Action[name=Play Cell 6] > 
Contains? false
value : null
< -------------
| 1 | O | X |
-------------
| 4 | X | O |
-------------
| X | O | X |
-------------
 , Action[name=Play Cell 6] > 
Contains? false
value : null

So, firsly, the two pairs look equals but can't be by definition of a Set, but how is it possible for the HashMap's KeySet (and same result replacing it with an HashTable, that can't admit null values so the "value : null" means that no such entry exists in the map) to not retrieve the pair which directly comes from Q.keySet()? 因此,首先,这两对看上去相等,但根据Set的定义却不能,但是HashMap的KeySet怎么可能(以及相同的结果用HashTable替换,它不能接受空值,所以“ :null”表示在地图中不存在这样的条目)以不检索直接来自Q.keySet()的货币对? Because even if there is a problem in an equals method that I don't see, using contains in this case would be like comparing an instance with itself using equals at some point, no? 因为即使在我没有看到的equals方法中有问题,在这种情况下使用contains就像在某个时候使用equals将实例与其自身进行比较,不是吗? So I guess the first "this == obj" condition would be checked. 因此,我猜第一个“ this == obj”条件将被检查。

I must be missing something, but I tried a lot of things and I have no more idea, so I hope someone will be able to help me... 我一定很想念东西,但是我尝试了很多事情,但我再也没有想法了,所以我希望有人能够帮助我...

Thank you for your precious time, 谢谢您的宝贵时间,

Paul 保罗

Edit 1 : as suggested by @tevemadar, I add prints in order to show hashCodes. 编辑1:如@tevemadar所建议,我添加打印以显示hashCodes。

    for(Pair<S, A> pair : Q.keySet()) {
        System.out.println(pair);
        System.out.println("Contain? " + Q.keySet().contains(pair));
        System.out.println("value : " + Q.get(pair));
        System.out.println("Pair hashCode : " + pair.hashCode());
        System.out.println("Grid hashCode : " + pair.getFirst().hashCode());
        System.out.println("Action hashCode : " + pair.getSecond().hashCode());
    } 

And here is a returned example : 这是一个返回的示例:

< -------------
| 1 | X | O |
-------------
| 4 | O | X |
-------------
| O | X | O |
-------------
 , Action[name=Play Cell 3] > 
Contain? false
value : null
Pair hashCode : 1710044996
Grid hashCode : 79268846
Action hashCode : -224488982
< -------------
| 1 | X | O |
-------------
| 4 | O | X |
-------------
| O | X | O |
-------------
 , Action[name=Play Cell 3] > 
Contain? false
value : null
Pair hashCode : 1710044996
Grid hashCode : 79268846
Action hashCode : -224488982

So both elements that both belong to the keySet() seem to have the same hashCodes too. 因此,两个都属于keySet()的元素似乎也具有相同的hashCodes。

Actually, this is because the key Pair in this case is mutable, and you can change it internal attributes. 实际上,这是因为在这种情况下,密钥Pair是可变的,因此您可以更改其内部属性。

Consider the below example, we have two keys, with different 'names', and then .put will not see them same, since they have different hashCode , however, similar to your case, you seem to change the attributes inside that pair, and in this case, the map will not remove the duplicate, since it does this only on put . 考虑下面的示例,我们有两个具有不同“名称”的键,然后.put将看不到它们,因为它们具有不同的hashCode ,但是,与您的情况类似,您似乎更改了该对中的属性,并且在这种情况下,映射将不会删除重复项,因为它仅在put上执行此操作。

class MyAction extends DynamicAction {
    public MyAction(String name) {
        super(name);
    }
}

@Test
public void test() {
    Map<Pair<String, MyAction>, Double> Q = new Hashtable<Pair<String, MyAction>, Double>();
    Pair<String, MyAction> pair1 = new Pair<String, MyAction>(
            "Play Cell", new MyAction("something1"));
    Pair<String, MyAction> pair2 = new Pair<String, MyAction>(
            "Play Cell", new MyAction("something2"));

    // different hashcodes
    System.out.println(pair1.hashCode());
    System.out.println(pair2.hashCode());
    Q.put(pair1, 1d);
    Q.put(pair2, 1d);
    // so the size is 2
    System.out.println(Q.size());

    // however, we can change the hashcode of the key afterwards
    System.out.println("Setting attributes");
    pair2.getSecond().setAttribute(DynamicAction.ATTRIBUTE_NAME, "something1");
    for (Object o : Q.keySet()) {
        Pair p = (Pair) o;
        System.out.println(p.hashCode());
    }
    // and the size is still 2
    System.out.println(Q.size());
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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