我有一个用Java实现的MineSweeper的完全工作版本。但是,我正在尝试添加一个更新Map的附加功能,以存储二维阵列中地雷位置的索引。例如,如果location [x] [y]拥有一个矿井,我将存储一个包含x和y的链表,该列表映射到一个布尔值,该布尔值为true表示该空间包含矿井。 (这个功能看似微不足道,但我只是用Java中的Collections来练习。)
我的相关私有实例变量包括:
public Class World{ ...
private LinkedList<Integer> index;
private Map<LinkedList<Integer>, Boolean> revealed;
“index”是要存储在地图中的列表,作为每个布尔值的键。
在我的构造函数中,我有:
public World(){ ...
tileArr = new Tile[worldWidth][worldHeight];
revealed = new TreeMap<LinkedList<Integer>, Boolean>();
index = new LinkedList<Integer>();
... }
现在,在我放置地雷的方法中,我有以下内容:
private void placeBomb(){
int x = ran.nextInt(worldWidth); //Random stream
int y = ran.nextInt(worldHeight); //Random stream
if (!tileArr[x][y].isBomb()){
tileArr[x][y].setBomb(true);
index.add(x); //ADDED COMPONENT
index.add(y);
revealed.put(index, true);
index.remove(x);
index.remove(y); //END OF ADDED COMPONENT
} else placeBomb();
}
如果没有标记添加的组件,我的程序运行正常,我有一个完全正常工作的游戏。但是,这个添加会给我以下错误。
Exception in thread "main" java.lang.ClassCastException: java.util.LinkedList
cannot be cast to java.lang.Comparable
如果有人可以帮助指出这个错误的位置,那将非常有帮助!这仅用于收集的其他练习,不需要运行游戏。
答案 0 :(得分:1)
这里实际上有3个问题。一个你知道的,一个你没有,另一个只是使用LinkedList
作为地图的关键是笨重的。
发生ClassCastException
是因为TreeMap
是一个有序集,并且要求其中的每个键都实现Comparable
接口,否则您必须提供自定义Comparator
}}。 LinkedList
未实现Comparable
,因此您会遇到异常。这里的解决方案可能是使用其他地图,例如HashMap
,或者您可以编写自定义Comparator.
自定义Comparator
可能是这样的:
revealed = new TreeMap<List<Integer>, Boolean>(
// sort by x value first
Comparator.comparing( list -> list.get(0) )
// then sort by y if both x values are the same
.thenComparing( list -> list.get(1) )
);
(我觉得有必要包括这个,这是一个更强大的例子,不依赖于特定索引的特定元素):
revealed = new TreeMap<>(new Comparator<List<Integer>>() {
@Override
public int compare(List<Integer> lhs, List<Integer> rhs) {
int sizeComp = Integer.compare(lhs.size(), rhs.size());
if (sizeComp != 0) {
return sizeComp;
}
Iterator<Integer> lhsIter = lhs.iterator();
Iterator<Integer> rhsIter = rhs.iterator();
while ( lhsIter.hasNext() && rhsIter.hasNext() ) {
int intComp = lhsIter.next().compareTo( rhsIter.next() );
if (intComp != 0) {
return intComp;
}
}
return 0;
}
});
您不知道的问题是,您只需向地图添加一个LinkedList
:
index.add(x);
index.add(y);
// putting index in to the map
// without making a copy
revealed.put(index, true);
// modifying index immediately
// afterwards
index.remove(x);
index.remove(y);
这是未指定的行为,因为您将密钥放入,然后进行修改。 Map
的文档说明了以下内容:
注意:如果将可变对象用作映射键,则必须非常小心。如果在对象是地图中的关键字时,以影响
equals
比较的方式更改对象的值,则不会指定地图的行为。
实际发生的事情(对于TreeMap
)是您总是删除之前的映射。 (例如,当您第一次拨打put
时,请说x=0
和y=0
。然后在下一次,您设置列表以便x=1
和{{1这也修改了地图中的列表,这样当调用y=1
时,它会发现已经有一个put
和x=1
的密钥并替换了映射。)
所以你可以通过说出以下任何一种方法来解决这个问题:
y=1
但是,这引出了我的第3点。
如果您想练习集合,有更好的方法可以做到这一点。一种方法是使用// copying the List called index
revealed.put(new LinkedList<>(index), true);
// this makes more sense to me
revealed.put(Arrays.asList(x, y), true);
,如下所示:
Map<Integer, Map<Integer, Boolean>>
这基本上就像一个2D数组,但有一个Map<Integer, Map<Integer, Boolean>> revealed = new HashMap<>();
{
revealed.computeIfAbsent(x, HashMap::new).put(y, true);
// the preceding line is the same as saying
// Map<Integer, Boolean> yMap = revealed.get(x);
// if (yMap == null) {
// yMap = new HashMap<>();
// revealed.put(x, yMap);
// }
// yMap.put(y, true);
}
。 (如果你有一个非常非常大的游戏板,这可能是有意义的。)
根据您的描述判断,听起来您已经知道可以在HashMap
课程中创建boolean isRevealed;
变量。
答案 1 :(得分:0)
从树形图的规格给出了这个:
地图根据其键的自然顺序或地图创建时提供的比较器进行排序,具体取决于使用的构造函数。
无法像那样比较Java Linkedlist。你必须给它一个比较它们的方法,或者只是使用另一种不需要排序的地图。