【Java必修课】HashMap性能很好?问过我EnumMap没

1 简介

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

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

其实能够综合上面三种状况,实际就是两种方法:code

  • (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返回的视图是有序的,这个顺序不是插入的顺序,而是枚举定义的顺序。代码以下:rem

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

输出的结果以下:get

北
南
东
西
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,性能会更好哦。


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

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

相关文章
相关标签/搜索