1

如果地图中不存在密钥,我正在尝试将数据添加到我的哈希图中。出于某种原因,即使密钥确实存在,哈希映射也会添加它。我不知道为什么会这样。我的 addEntity 方法是问题所在。我正在尝试检测密钥是否已经在哈希图中,如果是,则什么也不做。但是,由于某种原因,它总是会添加密钥,无论密钥是否已经存在。

我的数据文件:

package timeTraveler.mechanics;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import net.minecraft.entity.EntityLiving;

public class PathingData 
{
    /**
     * Entity data array
     */
    public static Map<String[], List<int[]>> allEntityData;

    public PathingData()
    {
        allEntityData = new HashMap<String[], List<int[]>>();
    }
    /**
     * Adds an entity UUID (Unique ID)and MobType to the entity data ArrayList.  If the entity already exists inside of the ArrayList, then it skips it.
     * @param uuid
     */
    public void addEntity(String[] entityData)
    {       
        System.out.println(entityData[0]);
        if(!allEntityData.containsKey(entityData))
        {
            System.out.println("Adding entity!");
            allEntityData.put(entityData, new ArrayList<int[]>());
        }
        else
        {
            System.out.println("ENTITY ALREADY EXISTS IN ARRAY");
        }
    }
    /**
     * Adds data (X, Y, and Z) to the corresponding UUID (Unique ID) for the entity.  If the entity's UUID does not exist, then it prints out a line that says the UUID cannot be found.
     * @param uuid
     * @param data
     */
    public void addData(String[] entityData, String data)
    {
        System.out.println(entityData[0]);
        if(allEntityData.containsKey(entityData))
        {
            System.out.println("Adding data to entity!");
            int[] rawData = new int[3];
            String[] pureData = data.split(",");

            rawData[0] = Integer.parseInt(pureData[0]);
            rawData[1] = Integer.parseInt(pureData[1]);
            rawData[2] = Integer.parseInt(pureData[2]);

            List<int[]> entityLocData = allEntityData.get(entityData);
            entityLocData.add(rawData);
            allEntityData.put(entityData, entityLocData);
        }
        else
        {
            System.out.println("ENTITY DOES NOT EXIST IN ARRAY! :(");
            //addEntity(entityData);
        }
    }
    /**
     * Gets the data for a specific UUID (Unique ID) for an entity.
     * @param uuid
     * @return
     */
    public List<int[]> getDataForUUID(String[] entityData)
    {
        List<int[]> entityLoc = allEntityData.get(entityData);
        return entityLoc;
    }
    /**
     * Clears all entities and their corresponding data from the map.
     */
    public void clearAllEntitiesAndData()
    {
        allEntityData.clear();
    }

    /**
     * Checks if entity exists inside of array
     * @param uuid
     * @return
     */
    public boolean doesEntityExist(String[] entityData)
    {
        List<int[]> entityLoc = allEntityData.get(entityData);
        if(entityData != null)
        {
            return true;
        }
        return false;
    }
}

我已确保该变量只有一个实例,并且我总是在我的 .addEntity 和 .addData 中引用该变量。有任何想法吗?

编辑:我刚刚尝试实施提出的建议。但是,它仍然只是打印出相同的内容,使用 timetraveler.core.StringArrayHolder@0 而不是数组。这是修改后的代码:

package timeTraveler.mechanics;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import timeTraveler.core.StringArrayHolder;

import net.minecraft.entity.EntityLiving;

public class PathingData 
{
    /**
     * Entity data array
     */
    public static Map<StringArrayHolder, List<int[]>> allEntityData;

    public PathingData()
    {
        allEntityData = new HashMap<StringArrayHolder, List<int[]>>();
    }
    /**
     * Adds an entity UUID (Unique ID)and MobType to the entity data ArrayList.  If the entity already exists inside of the ArrayList, then it skips it.
     * @param uuid
     */
    public void addEntity(StringArrayHolder entityData)
    {       
        System.out.println(entityData);
        if(!allEntityData.containsKey(entityData))
        {
            System.out.println("Adding entity!");
            allEntityData.put(entityData, new ArrayList<int[]>());
        }
        else
        {
            System.out.println("ENTITY ALREADY EXISTS IN ARRAY");
        }
    }
    /**
     * Adds data (X, Y, and Z) to the corresponding UUID (Unique ID) for the entity.  If the entity's UUID does not exist, then it prints out a line that says the UUID cannot be found.
     * @param uuid
     * @param data
     */
    public void addData(StringArrayHolder entityData, String data)
    {
        System.out.println(entityData);
        if(allEntityData.containsKey(entityData))
        {
            System.out.println("Adding data to entity!");
            int[] rawData = new int[3];
            String[] pureData = data.split(",");

            rawData[0] = Integer.parseInt(pureData[0]);
            rawData[1] = Integer.parseInt(pureData[1]);
            rawData[2] = Integer.parseInt(pureData[2]);

            List<int[]> entityLocData = allEntityData.get(entityData);
            entityLocData.add(rawData);
            allEntityData.put(entityData, entityLocData);
        }
        else
        {
            System.out.println("ENTITY DOES NOT EXIST IN ARRAY! :(");
            //addEntity(entityData);
        }
    }
    /**
     * Gets the data for a specific UUID (Unique ID) for an entity.
     * @param uuid
     * @return
     */
    public List<int[]> getDataForUUID(StringArrayHolder entityData)
    {
        List<int[]> entityLoc = allEntityData.get(entityData);
        return entityLoc;
    }
    /**
     * Clears all entities and their corresponding data from the map.
     */
    public void clearAllEntitiesAndData()
    {
        allEntityData.clear();
    }

    /**
     * Checks if entity exists inside of array
     * @param uuid
     * @return
     */
    public boolean doesEntityExist(StringArrayHolder entityData)
    {
        List<int[]> entityLoc = allEntityData.get(entityData);
        if(entityData != null)
        {
            return true;
        }
        return false;
    }
}

和包装:

package timeTraveler.core;

import java.util.ArrayList;

public class StringArrayHolder 
{
    private String[] data;

    public StringArrayHolder()
    {
        data = new String[2];
    }

    public void setData(String[] data)
    {
        this.data = data;
    }
    public String[] getData()
    {
        return this.data;
    }

    @Override
    public int hashCode() 
    {
        return 0;
        //...
    }

    @Override
    public boolean equals(Object o)
    {
        if(data.equals(o))
        {
            return true;
        }
        return false;
        //...
    }

}
4

1 回答 1

13

问题是数组不会覆盖类中的方法,因此即使您添加具有相同值的新数组equals,它也会是地图中的不同键。hashCodeObjectString[]

一个可能的解决方案是创建一个包装类,它将String[]为您保存并覆盖那里的equalsandhashCode方法。

public class MyStringArrayHolder {

    private String[] data;

   //class constructor...

   //getters and setters for the array...

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

    @Override
    public boolean equals(Object o) {
        //...
    }
}

对于equalsandhashCode方法的实现,可以在这个包装类中使用Arrays#equalsand 。Arrays#hashCode


从您的评论中:

我的 addEntity 方法是问题所在。我正在尝试检测密钥是否已经在哈希图中,如果是,则什么也不做。但是,由于某种原因,它总是会添加密钥,无论密钥是否已经存在。

这就是我上面解释过的。该方法Map#containsKey清楚地说明了这一点:

true当且仅当此映射包含一个键的映射k时才返回(key==null ? k==null : key.equals(k))

由于数组不会覆盖Object#equals,因此即使它们在同一位置具有相同的元素,您也不会有两个相似的数组键。


编辑:根据您当前的编辑,问题出在equalsandhashCode方法的实现中。我已经对该类进行了基本实现MyStringArrayHolder并复制/粘贴了PathingData该类的代码。这按预期工作(至少在这种情况下):

class MyStringArrayHolder {
    private final String[] data;
    //I do not want any client could change the array reference
    //this also explains why this field doesn't have a setter
    public MyStringArrayHolder(String[] data) {
        this.data = data;
    }
    public String[] getData() {
        return this.data;
    }
    @Override
    public int hashCode() {
        return Arrays.hashCode(data);
    }
    @Override
    public boolean equals(Object o) {
        if (o == null) return false;
        if (o == this) return true;
        if (o instanceof MyStringArrayHolder) {
            MyStringArrayHolder other = (MyStringArrayHolder)o;
            return Arrays.equals(this.data, other.data);
        }
        return false;
    }
    //just to print in console for testing purposes
    @Override
    public String toString() {
        return Arrays.deepToString(data);
    }
}

public class PathingData {
    //removed the static modifier, not really sure why you need it like that
    public Map<MyStringArrayHolder, List<int[]>> allEntityData;
    //current class implementation...
    //just to print in console for testing purposes
    @Override
public String toString() {
    return allEntityData.toString();
}
public static void main(String[] args) {
    PathingData pathingData = new PathingData();
    String[] example1 = { "hello", "world" };
    String[] example2 = { "luiggi", "mendoza" };
    String[] example3 = { "hello", "world" };
    MyStringArrayHolder holder1 = new MyStringArrayHolder(example1);
    MyStringArrayHolder holder2 = new MyStringArrayHolder(example2);
    MyStringArrayHolder holder3 = new MyStringArrayHolder(example3);
    pathingData.addEntity(holder1);
    pathingData.addEntity(holder2);
    pathingData.addEntity(holder3);
    pathingData.addData(holder1, "1,2,3");
    pathingData.addData(holder2, "4,5,6");
    pathingData.addData(holder3, "7,8,9");
    System.out.println(pathingData);
}
}

输出:

Adding entity!
Adding entity!
ENTITY ALREADY EXISTS IN ARRAY
Adding data to entity!
Adding data to entity!
Adding data to entity!
{[luiggi, mendoza]=[[I@35087359], [hello, world]=[[I@5a7691c0, [I@1e5b02a6]}

注意:包含的最后一行[I@35087359int[]. 我建议从更改List<int[]>List<List<Integer>>,但此实现超出了问题的范围:)。

于 2013-09-21T16:33:25.757 回答