
本文介绍如何改进骑士移动位置计算方法,使其仅返回合法坐标数组,不包含null元素,且不依赖arraylist等动态集合类。
本文介绍如何改进骑士移动位置计算方法,使其仅返回合法坐标数组,不包含null元素,且不依赖arraylist等动态集合类。
在实现国际象棋中骑士(Knight)的合法移动位置时,一个常见误区是预先分配固定长度数组(如new Position[8]),再逐个填入结果——这会导致非法移动位置被设为null,最终返回冗余、不紧凑的结果(如{(2,3),(3,2),null,null,...})。若受限于项目规范或教学要求不得使用ArrayList、LinkedList等动态集合类,我们可通过两阶段策略高效解决:先遍历统计合法移动数,再精确分配并填充结果数组。
以下是优化后的完整实现:
public Position[] moves(Position p) {
int row = p.getRow();
int col = p.getCol();
// 骑士8个可能的相对位移(行偏移, 列偏移)
int[][] offsets = {{-1,-2}, {1,-2}, {-1,2}, {1,2},
{-2,-1}, {-2,1}, {2,-1}, {2,1}};
// 第一阶段:统计合法移动数量
int validCount = 0;
for (int[] offset : offsets) {
int newRow = row + offset[0];
int newCol = col + offset[1];
if (newRow >= 1 && newRow <= 8 && newCol >= 1 && newCol <= 8) {
validCount++;
}
}
// 第二阶段:创建精确容量的数组,并再次遍历填充
Position[] result = new Position[validCount];
int index = 0;
for (int[] offset : offsets) {
int newRow = row + offset[0];
int newCol = col + offset[1];
if (newRow >= 1 && newRow <= 8 && newCol >= 1 && newCol <= 8) {
result[index++] = new Position(newRow, newCol);
}
}
return result;
}✅ 优势说明:
- 零null污染:返回数组长度严格等于实际合法移动数,调用方无需判空或过滤;
- 时间可控:两次线性遍历(O(1)常数级,因骑士最多8种移动),性能稳定;
- 内存高效:无额外对象开销(如ArrayList内部数组扩容);
- 完全符合约束:未引入任何java.util.Collection子类。
⚠️ 注意事项:
- 确保Position类的构造与getRow()/getCol()行为符合1-indexed棋盘约定(即行列范围为1–8);
- 若需支持0-indexed坐标(如数组下标风格),请将边界判断改为 >= 0 &&
- 此方案虽多一次循环,但相比Arrays.copyOfRange配合预分配+计数的变体(如先填满再截取),逻辑更清晰、可读性更高,也避免了临时null数组的创建。
总结:当无法使用动态集合时,“统计→分配→填充”三步法是处理已知上限但实际数量可变的返回数组问题的经典范式,兼顾健壮性、可维护性与教学示范价值。

















