lixmet
lixmet
发布于 2周前

Java HashMap需要注意的两个问题:迭代与键的选择

HashMap迭代方式的选择

HashMap迭代可以有两种方式:entrySet迭代和keySet迭代

示例

entrySet迭代

Map map = new HashMap();
Iterator it = map.entrySet().iterator();
while(it.hasNext()) {
  Map.Entry entry = (Map.Entry) it.next();
  Object key = entry.getKey();
  Object val = entry.getValue();
}

keySet迭代

Map map = new HashMap();
Iterator it = map.keySet().iterator();
while(it.hasNext()) {
  Object key = it.next();
  Object val = map.get(key);
}

这两种都可以实现迭代map,获取map里元素的键和值。它们两种主要差别在于迭代的效率上。

entrySet使用迭代器只做了一次遍历。而keySet实际做了两次,一次是使用迭代器迭代,另外一次是使用map.get(key)获取值是需要使用hashCode比较。所以效率上entrySet迭代相对keySet迭代要高。

总结:迭代HashMap推荐使用效率高的entrySet方式迭代。

HashMap键的选择

HashMap的键可以是任何对象类型。HashMap会对键对象计算hashCode,然后根据对象的hashCode做散列存储。

如下例子:

class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }

    public static void main(String[] args) {
      User jack = new User("Jack",18);
      Map<User,Integer> map = new HashMap<>();
      map.put(jack,100);
      jack.setAge(19);
      User user = map.get(jack);
      System.out.println(user);
    }

}

例子里的User重写了hashCode()方法,使用name和age计算对象的hashCode。HashMap使用User作为键。因为map存储User对象后,对对象的age做了修改,这样也会导致User对象的hashCode发生变化。但我们在使用map.get()方法获取数据时,得到的结果为null。

所以在选HashMap的键时需要选择键的hashCode固定的。这里推荐基本类型的类,如Integer和String,原因:

  1. Java已对这些类的hashCode重写,有比较好的散列效果
  2. 这些类的为final,不允许在对hashCode()方法重写,让hashCode的值固定
  3. 这些类为常用类型,在很多对象里会存在,如id,name等等。