Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 散列是合适的解决方案吗?我把事情复杂化了吗?_Java_Arrays_Hash_Concept - Fatal编程技术网

Java 散列是合适的解决方案吗?我把事情复杂化了吗?

Java 散列是合适的解决方案吗?我把事情复杂化了吗?,java,arrays,hash,concept,Java,Arrays,Hash,Concept,我写了一个2D平台游戏,我需要(最多4)个门的房间。我是用Java写的,但是语言是不相关的 每个房间可以有4个门,在顶部、底部和侧面。我叫他们北,南,东和西。当我建造一个房间时,我只给它一个整数,整数中的每一位代表一扇门 例如,如果我想要一个有三个门的房间 (一个在北面,一个在东面,一个在西面) 我给房间的号码是:11(二进制的1011) 因此,每个门都有一个整数来标识它 NORTH = 8;//1000 SOUTH = 4;//0100 EAST = 2;//0010 WEST = 1;/

我写了一个2D平台游戏,我需要(最多4)个门的房间。我是用Java写的,但是语言是不相关的

每个房间可以有4个门,在顶部、底部和侧面。我叫他们北,
西
。当我建造一个房间时,我只给它一个整数,整数中的每一位代表一扇门

例如,如果我想要一个有三个门的房间 (一个在北面,一个在东面,一个在西面) 我给房间的号码是:11(二进制的1011)

因此,每个门都有一个整数来标识它

NORTH = 8;//1000
SOUTH = 4;//0100
EAST =  2;//0010
WEST =  1;//0001
如果我生成一个房间,我会给它这些标识符的组合

例如:前面提到的房间将获得标识符

doorStock = NORTH | EAST | WEST;
我将这些门存储在一个简单的数组中:

Door doors[] = new Door[4];
我的问题是:我需要一个可以将标识符映射到数组中正确索引的函数。我并不总是需要4扇门

我一开始做的似乎是最简单的:doors数组将始终有4个元素,而我不使用的索引将保持为空

public Door getDoor(int doorID){
    switch(doorID){
        case NORTH:{
            return doors[0];
        }
        case SOUTH:{
            return doors[1];
        }
        case EAST:{
            return doors[2];
        }
        case WEST:{
            return doors[3];
        }
    }
    return null;
}
为了安全起见,我需要确定我请求的门是否真的存在于房间中

private boolean doorExists(int doorID){
    return (doorID & doorStock) != 0
}
因此,查询函数如下所示:

public Door getDoor(int doorID){
    switch(doorID){
        case NORTH:{
            if(doorExists(NORTH))return doors[0];
            else return null;
        }
        case SOUTH:{
            if(doorExists(NORTH))return doors[1];
            else return null;
        }
        case EAST:{
            if(doorExists(NORTH))return doors[2];
            else return null;
        }
        case WEST:{
            if(doorExists(NORTH))return doors[3];
            else return null;
        }
    }
    return null;
}
public Door getDoor(int doorID){
    switch(doorID){
        case NORTH:{
            if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[NORTH]]];
            else return null;
        }
        case SOUTH:{
            if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[SOUTH]]];
            else return null;
        }
        case EAST:{
            if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[EAST]]];
            else return null;
        }
        case WEST:{
            if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[WEST]]];
            else return null;
        }
    }
    return null;
}

这是有效的。但是这样,阵列可能会因未使用的元素而浪费空间。加上class
Door
可能是任意大小的,增加了内存浪费

更不用说我可能需要更多的门“插槽”(例如,如果我尝试在3D中实现此功能),因此我决定尝试根据门的标识符制作门阵列的大小:

Door doors = new Door[Integer.bitCount(doorStock)];
这很快就产生了一个
IndexOutOfBounds
错误。我并不惊讶,因为doors数组可以是0到4之间的任意大小,所以我需要一种新的哈希方法

我想出了两个哈希表,一个用于数组索引:

private final int[][] doorhash = {
    /* NORTH  SOUTH   EAST    WEST doorStock*/
    { -1,     -1,     -1,     -1} /*0000*/,
    { -1,     -1,     -1,      0} /*0001*/,
    { -1,     -1,      0,     -1} /*0010*/,
    { -1,     -1,      0,      1} /*0011*/,
    { -1,      0,     -1,     -1} /*0100*/,
    { -1,      0,     -1,      1} /*0101*/,
    { -1,      0,      1,     -1} /*0110*/,
    { -1,      0,      1,      2} /*0111*/,
    {  0,     -1,     -1,     -1} /*1000*/,
    {  0,     -1,     -1,      1} /*1001*/,
    {  0,     -1,      1,     -1} /*1010*/,
    {  0,     -1,      1,      2} /*1011*/,
    {  0,      1,     -1,     -1} /*1100*/,
    {  0,      1,     -1,      2} /*1101*/,
    {  0,      1,      2,     -1} /*1110*/,
    {  0,      1,      2,      3} /*1111*/
};
还有一个有助于上一个表的映射:

private final int[] directionHash = {
    -1, /*0000*/
     3, /*0001 - WEST*/
     2, /*0010 - EAST*/
    -1, /*0011*/
     1, /*0100 - SOUTH*/
    -1, /*0101*/
    -1, /*0110*/
    -1, /*0111*/
     0, /*1000 - NORTH*/
};
因此,我当前的映射函数如下所示:

public Door getDoor(int doorID){
    switch(doorID){
        case NORTH:{
            if(doorExists(NORTH))return doors[0];
            else return null;
        }
        case SOUTH:{
            if(doorExists(NORTH))return doors[1];
            else return null;
        }
        case EAST:{
            if(doorExists(NORTH))return doors[2];
            else return null;
        }
        case WEST:{
            if(doorExists(NORTH))return doors[3];
            else return null;
        }
    }
    return null;
}
public Door getDoor(int doorID){
    switch(doorID){
        case NORTH:{
            if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[NORTH]]];
            else return null;
        }
        case SOUTH:{
            if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[SOUTH]]];
            else return null;
        }
        case EAST:{
            if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[EAST]]];
            else return null;
        }
        case WEST:{
            if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[WEST]]];
            else return null;
        }
    }
    return null;
}
这似乎也很好,但我觉得有一个更简单的解决方案来解决这个问题,或者使用更少浪费的哈希表。我觉得这不是渐进式的灵活,因为它应该是,或者我过于复杂的事情。有什么更好的方法


谢谢你抽出时间

你肯定是设计过度了。例如,它可以通过字符来解析,并使用
char[4]
数组来存储(最多)4个不同的字符
n
s
w
e

您可以使用
String
收集关于门的信息,每个
char
将是一扇门(然后您可以有类似
“nnse”
的东西,这意味着北墙上有两扇门,一扇南门和一扇东门)

您还可以使用字符的
ArrayList
,并可以根据需要将其转换为数组。 这取决于你想用它做什么,但是有很多方法可以实现你想要做的

记住这一点

过早优化是万恶之源


您的解决方案可能是最节省空间的,但很可能是时间效率低下的,而且肯定是最不清晰的

一个简单的面向对象的方法是拥有一个
房间
类,有4个
布尔值
,可以是一个数组,也可以是你想要的独立数组

public class Room {

    //Consider an ENUM for bonus points
    public static int NORTH=0;
    public static int SOUTH=1;
    public static int EAST=2;
    public static int WEST=3;   

    private boolean[] hasDoor=new boolean[4];

    public Room(boolean northDoor,boolean southDoor,boolean eastDoor,boolean westDoor) {
        setHasDoor(northDoor,NORTH);
        setHasDoor(southDoor,SOUTH);
        setHasDoor(eastDoor,EAST);
        setHasDoor(westDoor,WEST);
    }

    public final  void setHasDoor(boolean directionhasDoor, int direction){
        hasDoor[direction]=directionhasDoor;
    }
    public final boolean  getHasDoor(int direction){
        return hasDoor[direction];
    }


}
没有阅读文档或方法的任何人都会清楚这是做什么的,这始终是您在第一时间应该瞄准的目标

然后,可以按如下方式使用

public static void main(String[] args){
    ArrayList<Room> rooms=new ArrayList<Room>();

    rooms.add(new Room(true,false,true,false));
    rooms.add(new Room(true,true,true,false));

    System.out.println("Room 0 has door on north side:"+rooms.get(0).getHasDoor(NORTH));

}

最重要的问题是,您是否关心以牺牲清晰的代码和可能的执行速度为代价来节省几千字节的内存?在内存不足的情况下,您可能会这样做,否则您不会这样做。

好吧,根据评论,我决定不让事情进一步复杂化。我将使用第一个解决方案,其中
doors
数组将有4个元素,映射函数为:

public Door getDoor(int doorID){
    switch(doorID){
        case NORTH:{
            if(doorExists(NORTH))return doors[0];
            else return null;
        }
        case SOUTH:{
            if(doorExists(NORTH))return doors[1];
            else return null;
        }
        case EAST:{
            if(doorExists(NORTH))return doors[2];
            else return null;
        }
        case WEST:{
            if(doorExists(NORTH))return doors[3];
            else return null;
        }
    }
    return null;
}
我只是觉得这是一个很好的理论问题。谢谢你的回复

您可以将其转换为:

public Door getDoor(int doorID){
switch(doorID){
    case NORTH:{
        return doors[0];
    }
    case SOUTH:{
        return doors[1];
    }
    case EAST:{
        return doors[2];
    }
    case WEST:{
        return doors[3];
    }
}
return null;
}
为此:

public Door getDoor(int doorID){
    int index = 0;
    int id = doorID;
    while(id > 1){
        if(id & 1 == 1)
            return null;
        id = id >>> 1;
        index++;
    }
return doors[index];
}

我甚至会将这些门建模为一个枚举,以允许将来使用其他类型的门。例如:

public enum Door { TOP, BOTTOM, LEFT, RIGHT };

public class Room {
    private Set<Door> doors = new HashSet<Door>;
    public Room(Door... doors) {
        //Store doors.
        this.doors = doors;
    }

    public boolean hasDoor(Door door) {
        return this.doors.contains(door);
    }
}
公共枚举门{上、下、左、右};
公共教室{
私有集doors=新哈希集;
公共房间(门…门){
//商店门。
这个。门=门;
}
公共门(门){
返回此.doors.contains(门);
}
}
枚举是您的朋友:

// Each possible direction - with Up/Down added to show extendability.
public enum Dir {
  North,
  South,
  East,
  West,
  Up,
  Down;
}

class Room {
  // The doors.
  EnumSet doors = EnumSet.noneOf(Dir.class);

  // Many other possible constructors.
  public Room ( Dir ... doors) {
    this.doors.addAll(Arrays.asList(doors));
  }

  public boolean doorExists (Dir dir) {
    return doors.contains(dir);
  }
}

在这里,让Enum为您完成繁重的工作是很自然的事。它们还提供了一个现成的
枚举集
,非常有效。

好的,我100%同意枚举是解决这个问题的方法。我唯一建议的是,特别是因为这是应用于游戏的逻辑,并且您可能需要将定义房间的信息存储在某个地方,所以使用二进制ID系统。使用这种系统,您可以存储房间可用的门的二进制表示形式。当您处理的项目最多只能有一个时,此系统运行良好。在您的情况下,一个房间每个方向只能有一扇门。如果将房间中每个门的所有二进制值相加,则可以存储该值,然后将该值返回到正确的枚举中

例如:

public enum Direction {

    NONE (0, 0),
    NORTH (1, 1),
    SOUTH (2, 2),
    EAST (3, 4),
    WEST (4, 8),
    NORTHEAST (5, 16),
    NORTHWEST (6, 32),
    SOUTHEAST (7, 64),
    SOUTHWEST (8, 128),
    UP (9, 256),
    DOWN (10, 512);

    private Integer id;
    private Integer binaryId;

    private Direction(Integer id, Integer binaryId) {
        this.id = id;
        this.binaryId = binaryId;
    }

    public Integer getId() {
        return id;
    }

    public Integer getBinaryId() {
        return binaryId;
    }

    public static List<Direction> getDirectionsByBinaryIdTotal(Integer binaryIdTotal) {
        List<Direction> directions = new ArrayList<Direction>();

        if (binaryIdTotal >= 512) {
            directions.add(Direction.DOWN);
            binaryIdTotal -= 512;
        }
        if (binaryIdTotal >= 256) {
            directions.add(Direction.UP);
            binaryIdTotal -= 256;
        }
        if (binaryIdTotal >= 128) {
            directions.add(Direction.SOUTHWEST);
            binaryIdTotal -= 128;
        }
        if (binaryIdTotal >= 64) {
            directions.add(Direction.SOUTHEAST);
            binaryIdTotal -= 64;
        }
        if (binaryIdTotal >= 32) {
            directions.add(Direction.NORTHWEST);
            binaryIdTotal -= 32;
        }
        if (binaryIdTotal >= 16) {
            directions.add(Direction.NORTHEAST);
            binaryIdTotal -= 16;
        }
        if (binaryIdTotal >= 8) {
            directions.add(Direction.WEST);
            binaryIdTotal -= 8;
        }
        if (binaryIdTotal >= 4) {
            directions.add(Direction.EAST);
            binaryIdTotal -= 4;
        }
        if (binaryIdTotal >= 2) {
            directions.add(Direction.SOUTH);
            binaryIdTotal -= 2;
        }
        if (binaryIdTotal >= 1) {
            directions.add(Direction.NORTH);
            binaryIdTotal -= 1;
        }

        return directions;
    }

}
公共枚举方向{
无(0,0),
北(1,1),
南部(2,2),
东(3,4),
西(4,8),
东北(5,16),
西北(6,32),
东南(7,64),
西南(8128),
上升(9256),
下降(10512);
私有整数id;
私有整数二进制ID;
专用方向(整数id、整数二进制id){
this.id=id;
this.binaryId=binaryId;
}
公共整数getId(){
返回id;
}
公共整数g