在 Java 的集合框架里除了 Collection 类族外还有 Map 类族,在 Java 中 Collection 类族表示存储着对象的各种集合数据结构,而 Map 类族则表示存储着键值对的映射表数据结构。
类族的意思是可以归为一大类的接口、抽象类、实现类。Collection 和 Map 本身都是接口,代表一大类具有共性的数据结构,一个代表集合另外一个代表映射表。而集合、映射表是有序的还是无序的、底层用什么数据结构,则是由类族里的各个实现类决定的,比如 ArrayList 的元素是有序的,HashSet 内的元素不可重复且无序等等。
聊明白了 Collection 和 Map 类族具体是什么后,进入主题,学习一下 Map。本文的大纲如下:
Map 类族的成员
下面的层级架构图可以很好地展示出 Map 类族里都有哪些接口,抽象类和实现类。
上图清晰地表示出了Map 类族里的 接口、抽象类、实现类还有它们相互之间的关系。
- Map 接口表示一个保存键值对(key-value)的映射表。Map 中不能包含重复的键;每个键最多只能映射到一个值。
- AbstractMap 是一个抽象类,它实现了 Map 接口中的核心 API。其它 Map 的实现类可以通过继承 AbstractMap 来减少重复编码。
- SortedMap 是继承自 Map 的接口,用来表示有序的键值对映射表。
- HashMap、TreeMap、LinkedHashMap 和 WeakHashMap 是具体的 Map 实现类,其中常用的是HashMap 和 TreeMap,另外两个能用到的场景很少见。
- Map.Entry 在上图用组合关系(Composition)表示了它是 Map 的构成元素。 Map.Entry 接口是 Map 接口中定义的内嵌接口,表示 Map 中的单个键值对实体。
HashMap 和 TreeMap
HashMap
HashMap 底层使用哈希表来存储元素。键和值可以是任何类型,也可以是 null 。HashMap 不保证元素的任何存储和遍历顺序,当集合发生变化时,元素的顺序可能会发生变化。存储在 HashMap 中元素被分装到了不同的桶中。元素的 hashCode() 方法确定它属于哪个桶。
举个简单的例子,比如哈希码从 1 到 100 的元素属于第一个桶,哈希码 101 到 200 的元素属于第二个桶,依此类推。以这种分桶的方式存储元素的意义在于,我们可以在搜索、删除元素时消除对不相关桶中的元素的影响,添加元素时如果不发生桶溢出,也不会影响到其他桶的元素。
TreeMap
TreeMap 是提供了顺序保证的键值对结构,其中的元素默认按照元素 Key 的自然排序顺序进行存储,也可以在构造时提供 Comparator 来指定元素的存储顺序。 TreeMap 底层使用平衡的红黑树来存储元素,因此元素插入时间和搜索时间都比较稳定。当需要使用存有大量数据的Map数据结构时,TreeMap 是一个不错的选择。
另外在 Set 章节中我们学习过的例子 TreeSet 其就是依赖 TreeMap 实现的,当时我们也演示过怎么给容器设置排序器(Comparator)。
HashMap 是所有 Map 实现中最快的,如果数据量不大,且对元素的顺序没有要求的场景中更推荐使用 HashMap。
创建 Map
创建 Map 就是创建 Map 接口的实现类的实例,下面是创建 HashMap 和 TreeMap 的示例。
Map mapA = new HashMap();
Map mapB = new TreeMap();
默认情况下,可以把任何对象放入Map,但是从 Java 5 开始,使用泛型可以限制 Map 中的键和值的对象类型。
Map<String, Student> map = new HashMap<>();
这个