本文共 14157 字,大约阅读时间需要 47 分钟。
Java 中的 Map 接口 是和 同一等级的集合根接口,它 表示一个键值对 (key-value) 的映射。
一个 Map 中,任意一个 key 都有唯一确定的 value 与其对应,这个 key-value 的映射就是 map。
Map 中元素的顺序取决于迭代器迭代时的顺序,有的实现类保证了元素输入输出时的顺序,比如说 TreeMap;有的实现类则是无序的,比如 HashMap。
Map 接口提供了三种角度来分析 Map:
KeySet 是一个 Map 中键(key)的集合,以 Set 的形式保存,不允许重复,因此键存储的对象需要重写 equals() 和 hashCode() 方法。
在上图就是保存 AA, BB, CC, DD… 等键的集合。
可以通过 Map.keySet() 方法获得。
Values 是一个 Map 中值 (value) 的集合,以 Collection 的形式保存,因此可以重复。
在上图就是保存 90,90,56,78… 等值的集合。
通过 Map.values() 方法获得。
Entry 是 Map 接口中的静态内部接口,表示一个键值对的映射,例如上图中 AA-90 这一组映射关系。
Entry 具有上图中的方法:
通过 Map.entrySet() 方法获得的是一组 Entry 的集合,保存在 Set 中,所以 Map 中的 Entry 也不能重复。
public Set> entrySet(); 
根据 Map 提供的三种视图,可以有三种 map 遍历方式 :
Set set = map.keySet();    for (Object key : set) {        System.out.println(map.get(key));    }   Collection values = map.values();    Iterator iterator = values.iterator();    while (iterator.hasNext()){        System.out.println("value " + iterator.next());    }   Set entrySet = map.entrySet();    for (Object o : entrySet) {        Map.Entry entry = (Map.Entry) o;        System.out.println(entry);      //key=value        System.out.println(entry.getKey() + " / " + entry.getValue());    }   public class LambdaMap {
private Map<String, Object> map = new HashMap<>();
@Before
public void initData() { map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", "value3"); map.put("key4", 4); map.put("key5", 5); map.put("key5", 'h'); } /** * 遍历Map的方式一 * 通过Map.keySet遍历key和value */ @Test public void testErgodicWayOne() { System.out.println("---------------------Before JAVA8 ------------------------------"); for (String key : map.keySet()) { System.out.println("map.get(" + key + ") = " + map.get(key)); } System.out.println("---------------------JAVA8 ------------------------------"); map.keySet().forEach(key -> System.out.println("map.get(" + key + ") = " + map.get(key))); }/**
* 遍历Map第二种 * 通过Map.entrySet使用Iterator遍历key和value */ @Test public void testErgodicWayTwo() { System.out.println("---------------------Before JAVA8 ------------------------------"); Iterator<Map.Entry<String, Object>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Object> entry = iterator.next(); System.out.println("key:value = " + entry.getKey() + ":" + entry.getValue()); } System.out.println("---------------------JAVA8 ------------------------------"); map.entrySet().iterator().forEachRemaining(item -> System.out.println("key:value=" + item.getKey() + ":" + item.getValue())); }/**
* 遍历Map第三种 * 通过Map.entrySet遍历key和value,在大容量时推荐使用 */ @Test public void testErgodicWayThree() { System.out.println("---------------------Before JAVA8 ------------------------------"); for (Map.Entry<String, Object> entry : map.entrySet()) { System.out.println("key:value = " + entry.getKey() + ":" + entry.getValue()); } System.out.println("---------------------JAVA8 ------------------------------"); map.entrySet().forEach(entry -> System.out.println("key:value = " + entry.getKey() + ":" + entry.getValue())); }/**
* 遍历Map第四种 * 通过Map.values()遍历所有的value,但不能遍历key */ @Test public void testErgodicWayFour() { System.out.println("---------------------Before JAVA8 ------------------------------"); for (Object value : map.values()) { System.out.println("map.value = " + value); } System.out.println("---------------------JAVA8 ------------------------------"); map.values().forEach(System.out::println); // 等价于map.values().forEach(value -> System.out.println(value)); }/**
* 遍历Map第五种 * 通过k,v遍历,Java8 */ @Test public void testErgodicWayFive() { System.out.println("---------------------Only JAVA8 ------------------------------"); map.forEach((k, v) -> System.out.println("key:value = " + k + ":" + v)); } }Map 的实现类主要有 4 种:
其中后三个的区别很类似 :
Map 的每个实现类都应该实现 2 个构造方法:
第二种构造方法允许我们复制一个 map。
虽然没有强制要求,但自定义 Map 实现类时最好都这样来。
Map 有以下特点:
注意:
可以使用 Map 作为 Map 的值,但禁止使用 Map 作为 Map 的键。因为在这么复杂的 Map 中,equals() 方法和 hashCode() 比较难定义。另一方面,你应该尽量避免使用“可变”的类作为 Map 的键。如果你将一个对象作为键值并保存在 Map 中,之后又改变了其状态,那么 Map 就会产生混乱,你所保存的值可能丢失。
AbstractMap 是 的实现类之一,也是 HashMap, TreeMap, ConcurrentHashMap 等类的父类。
AbstractMap 提供了 Map 的基本实现,使得我们以后要实现一个 Map 不用从头开始,只需要继承 AbstractMap, 然后按需求实现/重写对应方法即可。
AbstarctMap 中唯一的抽象方法:
public abstract Set> entrySet(); 
当我们要实现一个 不可变的 Map 时,只需要继承这个类,然后实现 entrySet() 方法,这个方法返回一个保存所有 key-value 映射的 set。 通常这个 Set 不支持 add(), remove() 方法,Set 对应的迭代器也不支持 remove() 方法。
如果想要实现一个 可变的 Map,我们需要在上述操作外,重写 put() 方法,因为 默认不支持 put 操作:
public V put(K key, V value) {    throw new UnsupportedOperationException();}   而且 entrySet() 返回的 Set 的迭代器,也得实现 remove() 方法,因为 AbstractMap 中的 删除相关操作都需要调用该迭代器的 remove() 方法。
正如其他集合推荐的那样,比如 ,实现类最好提供两种构造方法:
transient volatile SetkeySet;transient volatile Collection values; 
有两个成员变量:
他们都是 transient, volatile, 分别表示不可序列化、并发环境下变量的修改能够保证线程可见性。
需要注意的是 volatile 只能保证可见性,不能保证原子性,需要保证操作是原子性操作,才能保证使用 volatile 关键字的程序在并发时能够正确执行。
AbstractMap 中实现了许多方法,实现类会根据自己不同的要求选择性的覆盖一些。
接下来根据看看 AbstractMap 中的方法。
public V put(K key, V value) {    throw new UnsupportedOperationException();}public void putAll(Map    m) {    for (Map.Entry    e : m.entrySet())        put(e.getKey(), e.getValue());}   可以看到默认是不支持添加操作的,实现类需要重写 put() 方法。
public V remove(Object key) {    //获取保存 Map.Entry 集合的迭代器    Iterator            > i = entrySet().iterator();    Entry            correctEntry = null;    //遍历查找,当某个 Entry 的 key 和 指定 key 一致时结束    if (key==null) {        while (correctEntry==null && i.hasNext()) {            Entry              e = i.next();            if (e.getKey()==null)                correctEntry = e;        }    } else {        while (correctEntry==null && i.hasNext()) {            Entry                e = i.next();            if (key.equals(e.getKey()))                correctEntry = e;        }    }    //找到了,返回要删除的值    V oldValue = null;    if (correctEntry !=null) {        oldValue = correctEntry.getValue();        //调用迭代器的 remove 方法        i.remove();    }    return oldValue;}//调用 Set.clear() 方法清除public void clear() {    entrySet().clear();}                                 //时间复杂度为 O(n)//许多实现类都重写了这个方法public V get(Object key) {    //使用 Set 迭代器进行遍历,根据 key 查找    Iterator            > i = entrySet().iterator();    if (key==null) {        while (i.hasNext()) {            Entry            e = i.next();            if (e.getKey()==null)                return e.getValue();        }    } else {        while (i.hasNext()) {            Entry              e = i.next();            if (key.equals(e.getKey()))                return e.getValue();        }    }    return null;}                         //是否存在指定的 key//时间复杂度为 O(n)//许多实现类都重写了这个方法public boolean containsKey(Object key) {    //还是迭代器遍历,查找 key,跟 get() 很像啊    Iterator            > i = entrySet().iterator();    if (key==null) {        while (i.hasNext()) {            Entry            e = i.next();            //getKey()            if (e.getKey()==null)                return true;        }    } else {        while (i.hasNext()) {            Entry              e = i.next();            if (key.equals(e.getKey()))                return true;        }    }    return false;}//查询是否存在指定的值public boolean containsValue(Object value) {    Iterator                        > i = entrySet().iterator(); if (value==null) { while (i.hasNext()) { Entry                    e = i.next(); //getValue() if (e.getValue()==null) return true; } } else { while (i.hasNext()) { Entry                      e = i.next(); if (value.equals(e.getValue())) return true; } } return false;}public int size() { //使用 Set.size() 获取元素个数 return entrySet().size();}public boolean isEmpty() { return size() == 0;}                                                               //内部用来测试 SimpleEntry, SimpleImmutableEntry 是否相等的方法private static boolean eq(Object o1, Object o2) {    return o1 == null ? o2 == null : o1.equals(o2);}//判断指定的对象是否和当前 Map 一致//为什么参数不是泛型而是 对象呢//据说是创建这个方法时还没有泛型 - -public boolean equals(Object o) {    //引用指向同一个对象    if (o == this)        return true;    //必须是 Map 的实现类    if (!(o instanceof Map))        return false;    //强转为 Map    Map    m = (Map   ) o;    //元素个数必须一致    if (m.size() != size())        return false;    try {        //还是需要一个个遍历,对比        Iterator            > i = entrySet().iterator();        while (i.hasNext()) {            //对比每个 Entry 的 key 和 value            Entry            e = i.next();            K key = e.getKey();            V value = e.getValue();            if (value == null) {                //对比 key, value                if (!(m.get(key)==null && m.containsKey(key)))                    return false;            } else {                if (!value.equals(m.get(key)))                    return false;            }        }    } catch (ClassCastException unused) {        return false;    } catch (NullPointerException unused) {        return false;    }    return true;}//整个 map 的 hashCode() public int hashCode() {    int h = 0;    //是所有 Entry 哈希值的和    Iterator                     > i = entrySet().iterator();    while (i.hasNext())        h += i.next().hashCode();    return h;}                                 获取所有的键:
public SetkeySet() { //如果成员变量 keySet 为 null,创建个空的 AbstractSet if (keySet == null) { keySet = new AbstractSet () { public Iterator iterator() { return new Iterator () { private Iterator > i = entrySet().iterator(); public boolean hasNext() { return i.hasNext(); } public K next() { return i.next().getKey(); } public void remove() { i.remove(); } }; } public int size() { return AbstractMap.this.size(); } public boolean isEmpty() { return AbstractMap.this.isEmpty(); } public void clear() { AbstractMap.this.clear(); } public boolean contains(Object k) { return AbstractMap.this.containsKey(k); } }; } return keySet;} 
获取所有的值:
public Collectionvalues() { if (values == null) { //没有就创建个空的 AbstractCollection 返回 values = new AbstractCollection () { public Iterator iterator() { return new Iterator () { private Iterator > i = entrySet().iterator(); public boolean hasNext() { return i.hasNext(); } public V next() { return i.next().getValue(); } public void remove() { i.remove(); } }; } public int size() { return AbstractMap.this.size(); } public boolean isEmpty() { return AbstractMap.this.isEmpty(); } public void clear() { AbstractMap.this.clear(); } public boolean contains(Object v) { return AbstractMap.this.containsValue(v); } }; } return values;} 
获取所有键值对,需要子类实现:
public abstract Set> entrySet(); 
正如 中有内部类 Map.Entry 一样, AbstractMap 也有两个内部类:
SimpleImmutableEntry,不可变的键值对,实现了 Map.Entry < K,V> 接口:
public static class SimpleImmutableEntryimplements Entry , java.io.Serializable{ private static final long serialVersionUID = 7138329143949025153L; //key-value private final K key; private final V value; //构造函数,传入 key 和 value public SimpleImmutableEntry(K key, V value) { this.key = key; this.value = value; } //构造函数2,传入一个 Entry,赋值给本地的 key 和 value public SimpleImmutableEntry(Entry entry) { this.key = entry.getKey(); this.value = entry.getValue(); } //返回 键 public K getKey() { return key; } //返回 值 public V getValue() { return value; } //修改值,不可修改的 Entry 默认不支持这个操作 public V setValue(V value) { throw new UnsupportedOperationException(); } //比较指定 Entry 和本地是否相等 //要求顺序,key-value 必须全相等 //只要是 Map 的实现类即可,不同实现也可以相等 public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry )o; return eq(key, e.getKey()) && eq(value, e.getValue()); } //哈希值 //是键的哈希与值的哈希的 异或 public int hashCode() { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } //返回一个 String public String toString() { return key + "=" + value; }} 
SimpleEntry, 可变的键值对:
public static class SimpleEntryimplements Entry , java.io.Serializable{ private static final long serialVersionUID = -8499721149061103585L; private final K key; private V value; public SimpleEntry(K key, V value) { this.key = key; this.value = value; } public SimpleEntry(Entry entry) { this.key = entry.getKey(); this.value = entry.getValue(); } public K getKey() { return key; } public V getValue() { return value; } //支持 修改值 public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry )o; return eq(key, e.getKey()) && eq(value, e.getValue()); } public int hashCode() { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } public String toString() { return key + "=" + value; }} 
SimpleEntry 与 SimpleImmutableEntry 唯一的区别就是支持 setValue() 操作。
AbstractMap 是一个基础实现类,实现了 Map 的主要方法,默认不支持修改。
常用的几种 Map, 比如 HashMap, TreeMap, LinkedHashMap 都继承自它。