zoukankan      html  css  js  c++  java
  • 【Java必修课】HashMap性能很好?问过我EnumMap没

    1 简介

    我们知道Map只是一个接口,它有多种实现,Java中最常用的是HashMap了。而本文想讲述的是另一个实现:EnumMap。它是枚举类型的Map,要求它的Key值都必须是枚举型的。

    2 创建你的EnumMap

    既然是关于枚举类型的Map,我们先创建一个枚举,以便后续使用:

    public enum Directions {
        NORTH, SOUTH, EAST, WEST
    }
    

    2.1 创建EnumMap的三种方法

    JDK提供的创建EnumMap的方法有三种,代码如下:

    //new EnumMap
    EnumMap<Direction, String> enumMap = new EnumMap<>(Direction.class);
    enumMap.put(Direction.EAST, "东");
    enumMap.put(Direction.SOUTH, "南");
    //从EnumMap复制
    EnumMap<Direction, String> enumMapCopyEnumMap = new EnumMap<>(enumMap);
    assertEquals(enumMap, enumMapCopyEnumMap);
    //从Map复制
    Map<Direction, String> hashMap = Maps.newHashMap();
    hashMap.put(Direction.EAST, "东");
    hashMap.put(Direction.SOUTH, "南");
    EnumMap<Direction, String> enumMapCopyHashMap = new EnumMap<>(hashMap);
    assertEquals(enumMap, enumMapCopyHashMap);
    
    • (1) 使用new EnumMap()方法时,与HashMap不同,它必须传入一个枚举的类型才能创建对象;

    • (2) 从EnumMap复制,这时传入的参数为EnumMap

    • (3) 从Map复制,传入的参数为Map,但要求Key的类型必须是枚举型。

    2.2 聪明的Guava

    其实可以综合上面三种情况,实际就是两种方法:

    • (1) 使用new EnumMap(Class<K> keyType)

    • (2) 使用new EnumMap(Map<K, ? extends V> m)

    聪明的Guava就只提供了这两种方法,如下:

    //使用Guava创建
    EnumMap<Direction, String> enumMapGuava = Maps.newEnumMap(Direction.class);
    enumMapGuava.put(Direction.SOUTH, "南");
    assertEquals(1, enumMapGuava.size());
    enumMapGuava = Maps.newEnumMap(enumMap);
    assertEquals(enumMap, enumMapGuava);
    

    3 基本操作

    提供的方法与Map当然是一样的,操作十分方便,代码如下:

    @Test
    public void operations() {
      EnumMap<Direction, String> map = Maps.newEnumMap(Direction.class);
      //增加
      map.put(Direction.EAST, "东");
      map.put(Direction.SOUTH, "南");
      map.put(Direction.WEST, "西");
      //查询
      assertTrue(map.containsKey(Direction.EAST));
      assertFalse(map.containsKey(Direction.NORTH));
      //删除
      map.remove(Direction.EAST);
      assertFalse(map.containsKey(Direction.EAST));
      assertFalse(map.remove(Direction.WEST, "北"));
      assertTrue(map.remove(Direction.WEST, "西"));
      //清空
      map.clear();
      assertEquals(0, map.size());
    }
    

    需要特别指出的是删除方法,可以传入Key和Value两个参数,map.remove(Direction.WEST, "西")当键值对匹配时,则可以删除成功;map.remove(Direction.WEST, "北")匹配失败,则不会删除。

    4 集合视图

    4.1 有序性

    与Map接口提供的功能一样,EnumMap也能返回它的所有Values、Keys和Entry等。但与HashMap不同的是,EnumMap返回的视图是有序的,这个顺序不是插入的顺序,而是枚举定义的顺序。代码如下:

    EnumMap<Direction, String> map = Maps.newEnumMap(Direction.class);
    map.put(Direction.EAST, "东");
    map.put(Direction.SOUTH, "南");
    map.put(Direction.WEST, "西");
    map.put(Direction.NORTH, "北");
    //返回所有Value
    Collection<String> values = map.values();
    values.forEach(System.out::println);
    //返回所有Key
    Set<Direction> keySet = map.keySet();
    keySet.forEach(System.out::println);
    //返回所有<Key,Value>
    Set<Map.Entry<Direction, String>> entrySet = map.entrySet();
    entrySet.forEach(entry -> {
      System.out.println(entry.getKey() + ":" + entry.getValue());
    });
    

    输出的结果如下:

    北
    南
    东
    西
    NORTH
    SOUTH
    EAST
    WEST
    NORTH:北
    SOUTH:南
    EAST:东
    WEST:西
    

    这个顺序与我们定义枚举的顺序确实是一样的,而与添加的顺序无关。

    4.2 联动性

    除了有序性之外,EnumMap返回的集合视图还有一点不同就是联动性,即牵一发而动全身。改变其中一个,另外的也跟着变了。看代码一下就明白了:

    //Values、keySet、entrySet改变会影响其它
    values.remove("东");
    assertEquals(3, map.size());
    assertEquals(3, keySet.size());
    assertEquals(3, entrySet.size());
    
    keySet.remove(Direction.WEST);
    assertEquals(2, map.size());
    assertEquals(2, values.size());
    assertEquals(2, entrySet.size());
    
    entrySet.removeIf(entry -> Objects.equals(entry.getValue(), "北"));
    assertEquals(1, map.size());
    assertEquals(1, keySet.size());
    assertEquals(1, values.size());
    
    //Map的改变会影响其它视图
    map.clear();
    assertEquals(0, values.size());
    assertEquals(0, keySet.size());
    assertEquals(0, entrySet.size());
    

    5 性能

    性能是我们选择EnumMap的主要原因之一,那为何它性能会比优秀的HashMap还要好呢?通过看源码可以得知:

    (1)底层是通过两个数组来存放数据的,一个放Keys,一个放Values;

    (2)因为Key值是枚举类型,即一开始就确定了元素个数,所以在创建一个EnumMap的时候,存放数据的数组就已经确定了大小,不用考虑后续扩容带来的性能问题。

    (3)枚举本身就是固定顺序的,可以通过Enum.ordinal()方法获得顺序,这个便可以作为查询与插入的索引,而不用计算HashCode,性能也会比较快。这个顺序也就是数组下标。这也是EnumMap的集合视图都是有序的原因。

    (4)因为大小固定,则不用考虑加载因子,也不会有哈希冲突的问题,空间复杂度小。

    6 结论

    本文介绍了EnumMap作为一个Map的特殊实现的创建、使用、集合视图和性能分析,发现它的确是有过人之处的。当我们的Key值是枚举时,不妨可以试一试EnumMap,性能会更好哦。


    欢迎关注公众号<南瓜慢说>,将持续为你更新...

    多读书,多分享;多写作,多整理。

  • 相关阅读:
    利用Python获取文件类型
    删除目录和文件
    创建目录和空文件
    读取文件内容
    sort方法
    python实例5-表格打印
    ngx.lua中遇到的小问题
    mysql 更改自动增长字段值的重新设定
    全面讲解进程池原理
    测试工程师Docker基础
  • 原文地址:https://www.cnblogs.com/larrydpk/p/11815978.html
Copyright © 2011-2022 走看看