Java comparator uses non final variables

爱⌒轻易说出口 提交于 2019-12-12 17:25:40

问题


I want to sort a list, using a map which contains the values for each item.

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);
    }
})

I want the order to be 0,2,1 because the value of 1 is higher than 2. But java dont let me do this. I get the following error: Cannot refer to a non-final variable map inside an inner class defined in a different method

How can I do this a way like that?


回答1:


Just make it final:

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);
    }
})

As your map is mutable, you can still modify it.




回答2:


An annonymous inner class (your comparator is one) can only refer local variable that are declared final, in order to access your map - you must declare it as final.

Note that declaring it as final does not prevent you from modifying the map object, you just cannot assign a new object to the variable map.




回答3:


Your Comparator is an anonymous inner class. Inside it, you're trying to access a local variable, map, declared in the method that contains the anonymous inner class.

There's a restriction in Java that you can only do this if the local variable is final. So, make your variable map final:

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



回答4:


In your case the solution is trivial: mark your map as final:

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

You are probably confused with word final. It does not limit you in manipulations on your map. It just does not allow you to change reference to the map that is OK in your case.

The reason for this requirement is that your comparator is anonymous inner class. All outer method variables are copied to the of anonymous class, so if changing them in outer method will create conflicts. This is the reason that compiler requires marking the variable accessed from anonymous class as final.

Other solution is to extract your comparator to separate class and send map to it as an argument constructor.




回答5:


You can definitely do this. Just make a named class in place of your anonymous class extending Comparator and pass in the Map variable as a parameter. Like so:

  @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);
    }
  }

This prints out:

[0, 2, 1]

And also makes your code cleaner to read, IMO.



来源:https://stackoverflow.com/questions/12727299/java-comparator-uses-non-final-variables

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!