存储列表中的值时的比较错误,布尔映射

时间:2017-04-25 16:02:50

标签: java swing

我有一个用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

如果有人可以帮助指出这个错误的位置,那将非常有帮助!这仅用于收集的其他练习,不需要运行游戏。

2 个答案:

答案 0 :(得分:1)

这里实际上有3个问题。一个你知道的,一个你没有,另一个只是使用LinkedList作为地图的关键是笨重的。

  1. 发生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;
        }
    });
    
  2. 您不知道的问题是,您只需向地图添加一个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=0y=0。然后在下一次,您设置列表以便x=1和{{1这也修改了地图中的列表,这样当调用y=1时,它会发现已经有一个putx=1的密钥并替换了映射。)

    所以你可以通过说出以下任何一种方法来解决这个问题:

    y=1

    但是,这引出了我的第3点。

  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。你必须给它一个比较它们的方法,或者只是使用另一种不需要排序的地图。