Java arraylist 不保留我在方法中传递的值

Java arraylist does not keep values that I am passing inside a method

提问人:Stelios P.98 提问时间:6/25/2023 最后编辑:Stelios P.98 更新时间:6/28/2023 访问量:93

问:

我正在制作一个 AI 游戏,其中 cpu 使用统一成本搜索算法将表中的 N 个编号立方体堆叠成 3 行。我试图做的是该方法,它将返回一个 ArrayList,其中包含给定状态的所有可能移动。generateMoves()

Cube 是一个表示立方体的类,具有自己的 ID(立方体的编号)、X 轴位置和 Y 轴位置。

K 是用户输入

getCubes()是一个返回表当前状态的方法。ArrayList<Cube>

setCubes()是一种方法,它接受 并将该列表设置为当前状态。ArrayList<Cube>

isValid()是一个 Cube 方法,用于检查给定多维数据集是否处于有效位置的 copyOfTable 列表。

isFree()是一个 Cube 方法,用于检查给定多维数据集是否可以自由移动的 copyOfTable 列表。

public class Table{
  private fields

  constructor{}

  public Arraylist<Table> generateMoves(){
    Arraylist<Table> moves = new ArrayList<Table>();
    ArrayList<Cube> currentTable = this.getCubes();
    ArrayList<Cube> copyOfTable = new ArrayList<Cube>(currentTable);

    int X = 1;
    int Y = 1;
    int K = this.getK();

    for(Cube cube : currentTable){
      int backupX = cube.getPosX();
      int backupY = cube.getPosY();

      for(Y = 1; Y <= 3; Y++){
        for(X = 1; X <= 4*K; X++){
          cube.setPosX(X);
          cube.setPosY(Y);
          if(cube.isValid(copyOfTable) && cube.isFree(copyOfTable)){
            this.setCubes(currentTable);

            moves.add(this);

            moves.get(0).printTable();

          }else{
            cube.setPosX(backupX);
            cube.setPosY(backupY);
            continue;
          }
        }
      }
    }

    moves.get(anyIndex).printTable();

    return moves;
  }
}
// main is in a different file
main(){
  ArrayList<Table> moves = new ArrayList<Table>();
  Table table = new Table();
  
  moves = table.generateMoves();
}

我想要的是在 arraylist 中添加板的当前状态,以便我可以返回给定表可能生成的所有表(状态)。moves.add(this)moves

问题在于第一个(在第三个 for 循环中)打印了可以生成的每个状态。不仅是我要求的第一个。printTable()

第二个(在 return 语句上方),无论索引如何,都只打印表的第一个状态。printTable()

我在这里错过了什么?

我尝试了上面的代码

java arraylist 参数 - 传递 实例变量

评论

0赞 Valerij Dobler 6/25/2023
this不引用任何持久状态,而仅引用对正在使用的 Table 实例的引用。
3赞 Valerij Dobler 6/25/2023
要真正“保存”状态,您需要将 Table 类设计为不可变的,并在状态更改时创建新表。
0赞 Stelios P.98 6/26/2023
@ValerijDobler我创建了一个表,设置了它的状态并将其添加到 arraylist 中,但无济于事。我真的需要让我的整个班级成为决赛吗?newmoves
1赞 Tim Moore 6/26/2023
你不一定需要让整个班级成为最终课程,但你确实需要避免更改你不想更改的数据。
2赞 Valerij Dobler 6/26/2023
不可变意味着类及其字段不会在内部或外部更改。这意味着,如果您的字段包含集合,则必须在返回之前防御性地复制它们。使类成为最终类也有助于将其扩展为可变状态。看看 jdk15 记录。

答:

0赞 Reilas 6/26/2023 #1

"...我在这里错过了什么?..."

您的代码是正确的,您只是误解了 generateMoves 方法的上下文。
对于每次调用,返回的 ArrayList 不会保留,每次调用都会有一个新的 ArrayList

这种类型的方法需要放在不同的类中。 其中,您可以利用 ArrayList 类字段来填充列表。

请考虑以下几点。
通过将 Table 对象传递给 generateMoves,可以填充类字段移动

class TableUtil {
    static ArrayList<Table> moves = new ArrayList<>();

    static ArrayList<Table> getMoves() {
        return moves;
    }
    
    static void generateMoves(Table table) {
        ArrayList<Cube> currentTable = table.getCubes();
        ArrayList<Cube> copyOfTable = new ArrayList<Cube>(currentTable);

        int X = 1;
        int Y = 1;
        int K = table.getK();

        for(Cube cube : currentTable){
            int backupX = cube.getPosX();
            int backupY = cube.getPosY();

            for(Y = 1; Y <= 3; Y++){
                for(X = 1; X <= 4*K; X++){
                    cube.setPosX(X);
                    cube.setPosY(Y);
                    if(cube.isValid(copyOfTable) && cube.isFree(copyOfTable)){
                        table.setCubes(currentTable);

                        moves.add(table);

                        moves.get(0).printTable();

                    }else{
                        cube.setPosX(backupX);
                        cube.setPosY(backupY);
                        continue;
                    }
                }
            }
        }

        moves.get(table.anyIndex).printTable();
    }
}

创建 Table 对象,然后为每个实例调用 generateMoves 方法。

例如

Table tableA = new Table();
Table tableB = new Table();
Table tableC = new Table();

TableUtil.generateMoves(tableA);
TableUtil.generateMoves(tableB);
TableUtil.generateMoves(tableC);

ArrayList<Table> moves = TableUtil.getMoves();

评论

0赞 Stelios P.98 6/26/2023
这被认为是好的做法,还是我处于界限模糊的极端情况下?
0赞 Stelios P.98 6/26/2023
另外,既然我们有一个 ,为什么我们需要在方法中创建另一个普通的呢?static ArrayList movesgenerateMoves
1赞 Reilas 6/27/2023
@SteliosP.98,是的,当然有很多方法可以利用数据,尽管这种设计被称为“实用程序类”。en.wikipedia.org/wiki/Helper_class
1赞 Reilas 6/27/2023
@SteliosP.98,就静态移动而言,你是对的,我忘了删除局部变量。因此,您需要附加类字段。
1赞 Reilas 6/27/2023
@SteliosP.98,我不确定我是否完全理解您正在创建的应用程序。我建议在第一个 for 循环上设置一个断点并单步执行每一行。我相信你会找到不准确的地方。此外,您可以重写 TableCubetoString 方法,这将允许您使用 System.out.println(moves)...println(currentTable) 中。
0赞 Stelios P.98 6/28/2023 #2

我不知道为什么(请有人向我解释一下),但是在我花了很长时间的反复试验之后,解决方案是制作一种方法来创建有问题的 ArrayList 的“深度”副本,如下所示:

private ArrayList<Cube> createCopyOfTable(ArrayList<Cube> toCopy){
        ArrayList<Cube> copy = new ArrayList<>();

        for(Cube cube : toCopy){
            Cube _cube = new Cube(cube.getID(), cube.getPosX(), cube.getPosY());
            copy.add(_cube);
        }

        return copy;
    }

以及我的方法的更新部分:generateMoves()

.
.
.
for(X = 1; X <= 4*K; X++){
    cube.setPosX(X);
    cube.setPosY(Y);

    ArrayList<Cube> copyOfTable = createCopyOfTable(currentTable);

    if(cube.isValid(copyOfTable) && cube.isFree(copyOfTable)){
        Table table = new Table(K);
        table.setCubes(copyOfTable);
                        
        this.moves.add(table);
    }else{
        cube.setPosX(backupX);
        cube.setPosY(backupY);
        continue;
    }
}
.
.
.

评论

0赞 kanaparthikiran 6/30/2023
ArrayList clone() 方法用于创建列表的浅拷贝。在新列表中,仅复制对象引用。如果我们更改第一个 ArrayList 中的对象状态,则更改的对象状态也将反映在克隆的 ArrayList 中。如果我们想将内容从源列表到目标列表分开保存,则需要深层复制。