【问题标题】:Java comparator uses non final variablesJava 比较器使用非最终变量
【发布时间】:2012-09-25 11:47:13
【问题描述】:

我想使用包含每个项目的值的地图对列表进行排序。

Map<Integer, Float> map = new HashMap<>();
List<Integer> list = new ArrayList<>();

map.put(0, 0.0f);
map.put(1, 5.0f);
map.put(2, 2.0f);

list = new ArrayList<>(map.keySet());

Collections.sort(list, new Comparator<Integer>() {
    public int compare(Integer left, Integer right) {
        Float leftCost = map.get(left);
        Float rightCost = map.get(right);
        return leftCost.compareTo(rightCost);
    }
})

我希望订单为0,2,1,因为1 的值高于2。但是java不让我这样做。我收到以下错误:Cannot refer to a non-final variable map inside an inner class defined in a different method

我怎样才能以这种方式做到这一点?

【问题讨论】:

  • 为什么不将地图定义为最终地图?
  • 因为我需要修改。或者即使我修改它也可以是最终的?
  • @OskarKjellin 为什么不回答而不是评论? :)
  • @omnosis final 指的是引用,而不是它指向的对象。
  • 可以修改,但不能重新定义

标签: java list sorting collections final


【解决方案1】:

您的Comparator 是一个匿名内部类。在其中,您尝试访问在包含匿名内部类的方法中声明的局部变量 map

Java 中有一个限制,即只有当局部变量为final 时才能执行此操作。所以,让你的变量mapfinal

final Map<Integer, Float> map = new HashMap<>();

【讨论】:

    【解决方案2】:

    让它成为最终的:

    final Map<Integer, Float> map = new HashMap<Integer, Float>();
    List<Integer> list = new ArrayList<Integer>(); // this assignment is unncessary [1]
    
    map.put(0, 0.0f);
    map.put(1, 5.0f);
    map.put(2, 2.0f);
    
    list = new ArrayList<Integer>(map.keySet()); // 1. assignment is replaced here
    
    Collections.sort(list, new Comparator<Integer>() {
        public int compare(Integer left, Integer right) {
            Float leftCost = map.get(left);
            Float rightCost = map.get(right);
            return leftCost.compareTo(rightCost);
        }
    })
    

    由于您的地图是可变的,您仍然可以对其进行修改。

    【讨论】:

      【解决方案3】:

      匿名内部类(您的比较器是一个)只能引用声明为 final 的局部变量,以便访问您的地图 - 您必须将其声明为 final

      请注意将其声明为final 不会阻止您修改地图对象,您只是不能将新对象分配给变量map

      【讨论】:

        【解决方案4】:

        在您的情况下,解决方案很简单:将您的地图标记为final

        final Map&lt;Integer, Float&gt; map = new HashMap&lt;&gt;();

        您可能对 final 这个词感到困惑。它不会限制您对地图的操作。它只是不允许您更改对您的情况可以的地图的引用。

        此要求的原因是您的比较器是匿名内部类。所有外部方法变量都被复制到匿名类中,因此如果在外部方法中更改它们会产生冲突。这就是编译器要求将从匿名类访问的变量标记为 final 的原因。

        其他解决方案是将您的比较器提取到单独的类并将映射作为参数构造函数发送给它。

        【讨论】:

          【解决方案5】:

          你绝对可以做到这一点。只需创建一个命名类来代替扩展Comparator 的匿名类,并将Map 变量作为参数传递。像这样:

            @Test
            public void test() {
              Map<Integer, Float> map = new HashMap<Integer, Float>();
              map.put(0, 0.0f);
              map.put(1, 5.0f);
              map.put(2, 2.0f);
          
              List<Integer> list = new ArrayList<Integer>(map.keySet());
              Collections.sort(list, new FloatComparator(map));
          
              System.out.println(list);
            }
          
            class FloatComparator implements Comparator<Integer> {
              private Map<Integer, Float> mapRef;
              public FloatComparator(Map<Integer, Float> newMap) {
                mapRef = newMap;
              }
          
              @Override
              public int compare(Integer left, Integer right) {
                Float leftCost = mapRef.get(left);
                Float rightCost = mapRef.get(right);
                return leftCost.compareTo(rightCost);
              }
            }
          

          打印出来:

          [0, 2, 1]
          

          IMO,还可以让您的代码更清晰易读。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-11-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多