Map in Java

I. Map

java.util.Map<K,V>:

  • Map 是一个顶层的接口,不是 Collection 的子接口,用来存储键值对 (key - value)
  • 将键 Key 映射到值的对象,一个映射不能包含重复的值,每个键最多只能映射到一个值
  • K: 此映射所维护的键 key 的类型
  • V: 映射值 value 的类型
  • Map 中 key 不可重复, value 可以重复

II. Map 和 Collection 的区别

  • Map
    • 集合存储的元素是成对出现的,Map集合的键是唯一的,值是可以重复的
    • Map 集合的数据结构只针对键 key 有效,跟值无关
  • Collection
    • 集合存储的元素是单独出现的
    • Collection 的实现类 Set 的值是不可以重复的,List是可以重复的
    • Collection 的数据结构是针对元素有效

III. Map 功能概述

  • 添加功能
    • V put(K key, V value): 添加元素,存储时,如果key不存在,直接存储; 如果key存在,就把值替换成新值,返回旧值
    • void putAll(Map<? extends K,? extends V> m) : 添加一个map中所有的键值对
  • 删除功能
    • void clear() : 移除所有的键值对
    • void remove(Object key): 根据键删除键值对,并返回键值对中的值
  • 判断功能
    • boolean containsKey(Object key) : 判断集合是否包含指定的键
    • boolean containsValue(Object value) : 判断集合是否包含指定的值
    • boolean isEmpty() : 判断集合是否为空
  • 获取功能
    • Set<Map.Entry<K,V>> entrySet() : 获得键值对的 set 集合
    • Set<K> keySet() :获取集合中所有键的 set 集合
    • Collection<V> values() : 获取集合中所有值的 collection 集合
    • V get(Object key) : 返回指定键所映射的值,根据键获取值
  • 长度功能
    • int size() : 返回集合中键值对的对数

IV. Map 的实现类

1. HashMap

java.util.HashMap<K,V>:

  • 基于哈希表的 Map 接口的实现
  • 此实现提供所有可选的映射操作,并允许使用 null 值和 null 键
  • 此类不保证映射的顺序,特别是它不保证该顺序恒久不变,就是无序

hashmap 构造方法

  • public HashMap() :构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap
  • public HashMap(Map<? extends K,? extends V> m) :构造一个映射关系与指定 Map 相同的新 HashMap。所创建的 HashMap 具有默认加载因子 (0.75) 和足以容纳指定 Map 中映射关系的初始容量
  • public HashMap(int initialCapacity) :构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap
  • public HashMap(int initialCapacity, float loadFactor) :构造一个带指定初始容量和加载因子的空 HashMap

hashmap 示例

package org.lovian.collections.map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class MapDemo {
	public static void main(String[] args) {
		Map<Integer, String> map = new HashMap<>();

		// add key-value
		System.out.println("put 1: " + map.put(1, "hello"));
		System.out.println("put 1: " + map.put(1, "java"));
		System.out.println("put 2: " + map.put(2, "world"));
		System.out.println("put 3: " + map.put(3, "javaee"));
		System.out.println("put 4: " + map.put(4, "c++"));

		System.out.println("map: " + map);
		// size
		System.out.println("map size: " + map.size());

		System.out.println("==============");


		// remove
		System.out.println("remove 4: " + map.remove(4));
		System.out.println("remove 5: " + map.remove(5));
		System.out.println("map: " + map);
		System.out.println("==============");

		// contains
		System.out.println("contains3: " + map.containsKey(3));
		System.out.println("contains4: " + map.containsKey(4));
		System.out.println("contains java: " + map.containsValue("javaee"));
		System.out.println("contains python: " + map.containsValue("python"));

		System.out.println("==============");

		// get
		System.out.println("get 1: " + map.get(1));
		System.out.println("get 5: " + map.get(5));
		System.out.println("==============");

		// keySet
		Set<Integer> set = map.keySet();
		System.out.println("set: " + set);
		System.out.println("==============");

		// values
		Collection<String> col = map.values();
		System.out.println("values: " + col);
		System.out.println("==============");

		// entryset
		Set<Entry<Integer, String>> entrySet = map.entrySet();
		System.out.println("entry set: " + entrySet);

		// traverse map (by using entryset)
		for (Entry<Integer, String> entry : entrySet) {
			Integer key = entry.getKey();
			String value = entry.getValue();
			System.out.println("key: " + key + " value: " + value);
		}

		System.out.println("==============");
		map.clear();
		System.out.println("map: " + map);

	}
}

result

put 1: null
put 1: hello
put 2: null
put 3: null
put 4: null
map: {1=java, 2=world, 3=javaee, 4=c++}
map size: 4
==============
remove 4: c++
remove 5: null
map: {1=java, 2=world, 3=javaee}
==============
contains3: true
contains4: false
contains java: true
contains python: false
==============
get 1: java
get 5: null
==============
set: [1, 2, 3]
==============
values: [java, world, javaee]
==============
entry set: [1=java, 2=world, 3=javaee]
key: 1 value: java
key: 2 value: world
key: 3 value: javaee
==============
map: {}

遍历 map 有两种方式,一个是拿到 key 的 set, 通过 keySet 去找值; 另一种方式是拿到 key-value 的 entrySet集合,然后对 entrySet 去遍历 key 和 value

2. LinkedHashMap

java.util.LinkedHashMap<K,V>:

  • Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序
  • 哈希表保证唯一性:不重复
  • 链表保证有序性: 有序 (存储和取出顺序一致)

3. TreeMap

java.util.TreeMap<K,V>:

  • 基于红黑树(Red-Black tree)的 NavigableMap 实现
  • 该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
  • 基于红黑树,key可以排序,分自然排序和比较器排序,与 TreeSet 相同
  • 元素保持唯一,不重复

V. 练习

1. 获取字符串中每一个字母出现的次数要求结果

Q: 有字符串 “aababcabcdabcde” , 要求结果为 “a(5)b(4)c(3)d(2)e(1)” 思路:

  • 定义一个TreeMap集合,以字符作为 key, 出现的 count 作为 value
  • 将字符串转化为字符数组,遍历字符数组,然后将字符出现的次数存储到对应的 count 中
  • 定义 stringbuffer 变量
  • 遍历map集合,得到key value,进行字符串拼接
  • 将stringbuffer 转换成 string 进行输出

为什么这里要选用 treemap, 原因是因为排序的字符是有顺序的, (自然顺序排序), 所以这里要用treemap集合对键进行自然排序

2. Hashtable 和 HashMap 的区别

Hashtable

java.util.Hashtable<K,V>:

  • 注意 Hashtable 这里 t 是小写
  • 此类实现一个哈希表,用来存储 key - value, null 不可作为键值

Hashtable 用法和 HashMap 基本相同,实际上 HashMap 就是用来代替 Hashtable 的

区别

  • Hashtable
    • 线程安全,效率低
    • 不允许 null 键, null 值, null 会抛出 NullPointerException 异常
  • HashMao
    • 线程不安全,效率高
    • 允许 null 键和 null 值

VI. 集合的嵌套

Map 集合可以作为 Map 集合的 value 从而进行嵌套。同样的,collection 集合也可以作为 map 集合的 value 进行嵌套


Share this on