Java集合框架-Map架构

148 阅读10分钟

一、前言

站在巨人的肩膀上,本系列的Java集合框架文章参考了skywang12345——Java 集合系列,真心讲的很好,可以参考。但是不足的是,时间过于长久了,大佬的文章是基于JDK1.6.0_45,对于现在来说至少都用JDK 8.0以上了,而且JDK 6.0与JDK 8.0中集合框架的改动挺大的,所以本系列的文章是基于JDK_1.8.0_161进行说明的。

二、介绍

先来看看Map架构关系图,图片来源于Java 集合系列09之 Map架构

  • Map是映射接口,Map中存储的内容是键值对(key-value)。
  • AbstractMap是实现了Map接口的抽象类,它实现了Map中的大部分API。其它Map的实现类可以通过继承AbstractMap来减少重复编码。
  • SortedMap是继承于Map的接口。SortedMap中的内容是排序的键值对,排序的方法是通过比较器(Comparator)。
  • TreeMap继承于AbstractMap,且实现了NavigableMap接口;因此TreeMap中的内容是“有序的键值对”!
  • HashMap继承于AbstractMap,但没实现NavigableMap接口;因此,HashMap的内容是“键值对,但不保证次序”!
  • Hashtable虽然不是继承于AbstractMap,但它继承于Dictionary(Dictionary也是键值对的接口),而且也实现Map接口;因此Hashtable的内容也是“键值对,也不保证次序”。但和HashMap相比,Hashtable是线程安全的,而且它支持通过Enumeration去遍历。
  • WeakHashMap继承于AbstractMap。它和HashMap的键类型不同,WeakHashMap的键是“弱键”。

三、解析

1、Map

先来看看Map的接口定义:

public interface Map<K,V>
  • Map是一个键值对(key-value)映射接口。Map映射中不能包含重复的键;每个键最多只能映射到一个值。
  • Map接口提供三种collection视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。
  • Map映射顺序。有些实现类,可以明确保证其顺序,如TreeMap;另一些映射实现则不保证顺序,如HashMap 类。
  • Map的实现类应该提供2个“标准的”构造方法:第一个,void(无参数)构造方法,用于创建空映射;第二个,带有单个Map类型参数的构造方法,用于创建一个与其参数具有相同键-值映射关系的新映射。实际上,后一个构造方法允许用户复制任意映射,生成所需类的一个等价映射。尽管无法强制执行此建议(因为接口不能包含构造方法),但是JDK中所有通用的映射实现都遵从它。
int size();

boolean isEmpty();

boolean containsKey(Object key);

boolean containsValue(Object value);

V get(Object key);

V put(K key, V value);

V remove(Object key);

void putAll(Map<? extends K, ? extends V> m);

void clear();

Set<K> keySet();

Collection<V> values();

Set<Map.Entry<K, V>> entrySet();

boolean equals(Object o);

int hashCode();

/**
 * @since 1.8
 */
default V getOrDefault(Object key, V defaultValue) {
	V v;
	return (((v = get(key)) != null) || containsKey(key)) ? v : defaultValue;
}

/**
 * @since 1.8
 */
default void forEach(BiConsumer<? super K, ? super V> action) {
	Objects.requireNonNull(action);
	for (Map.Entry<K, V> entry : entrySet()) {
		K k;
		V v;
		try {
			k = entry.getKey();
			v = entry.getValue();
		} catch(IllegalStateException ise) {
			// this usually means the entry is no longer in the map.
			throw new ConcurrentModificationException(ise);
		}
		action.accept(k, v);
	}
}

/**
* @since 1.8
*/
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
	Objects.requireNonNull(function);
	for (Map.Entry<K, V> entry : entrySet()) {
		K k;
		V v;
		try {
			k = entry.getKey();
			v = entry.getValue();
		} catch(IllegalStateException ise) {
			// this usually means the entry is no longer in the map.
			throw new ConcurrentModificationException(ise);
		}

		v = function.apply(k, v);
		try {
			entry.setValue(v);
		} catch(IllegalStateException ise) {
			// this usually means the entry is no longer in the map.
			throw new ConcurrentModificationException(ise);
		}
	}
}

/**
 * @since 1.8
 */
default V putIfAbsent(K key, V value) {
	V v = get(key);
	if (v == null) {
		v = put(key, value);
	}
	return v;
}

/**
 * @since 1.8
 */
default boolean remove(Object key, Object value) {
	Object curValue = get(key);
	if (!Objects.equals(curValue, value) || (curValue == null && !containsKey(key))) {
		return false;
	}
	remove(key);
	return true;
}

/**
 * @since 1.8
 */
default boolean replace(K key, V oldValue, V newValue) {
	Object curValue = get(key);
	if (!Objects.equals(curValue, oldValue) || (curValue == null && !containsKey(key))) {
		return false;
	}
	put(key, newValue);
	return true;
}

/**
 * @since 1.8
 */
default V replace(K key, V value) {
	V curValue;
	if (((curValue = get(key)) != null) || containsKey(key)) {
		curValue = put(key, value);
	}
	return curValue;
}

/**
 * @since 1.8
 */
default V computeIfAbsent(K key,Function<? super K, ? extends V> mappingFunction) {
	Objects.requireNonNull(mappingFunction);
	V v;
	if ((v = get(key)) == null) {
		V newValue;
		if ((newValue = mappingFunction.apply(key)) != null) {
			put(key, newValue);
			return newValue;
		}
	}
	return v;
}

/**
* @since 1.8
*/
default V computeIfPresent(K key,BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
	Objects.requireNonNull(remappingFunction);
	V oldValue;
	if ((oldValue = get(key)) != null) {
		V newValue = remappingFunction.apply(key, oldValue);
		if (newValue != null) {
			put(key, newValue);
			return newValue;
		} else {
			remove(key);
			return null;
		}
	} else {
		return null;
	}
}

/**
 * @since 1.8
 */
default V compute(K key,BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
	Objects.requireNonNull(remappingFunction);
	V oldValue = get(key);
	V newValue = remappingFunction.apply(key, oldValue);
	if (newValue == null) {
		// delete mapping
		if (oldValue != null || containsKey(key)) {
			// something to remove
			remove(key);
			return null;
		} else {
			// nothing to do. Leave things as they were.
			return null;
		}
	} else {
		// add or replace old mapping
		put(key, newValue);
		return newValue;
	}
}

/**
 * @since 1.8
 */
default V merge(K key, V value,BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
	Objects.requireNonNull(remappingFunction);
	Objects.requireNonNull(value);
	V oldValue = get(key);
	V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value);
	if(newValue == null) {
		remove(key);
	} else {
		put(key, newValue);
	}
	return newValue;
}

说明:1、Map提供接口分别用于返回键集、值集或键-值映射关系集。entrySet()用于返回键-值集的Set集合。keySet()用于返回键集的Set集合。values()用户返回值集的Collection集合。因为Map中不能包含重复的键;每个键最多只能映射到一个值。所以,键-值集、键集都是Set,值集时Collection。
2、Map提供了“键-值对”、“根据键获取值”、“删除键”、“获取容量大小”等方法。
3、Map中新添加了很多jdk 1.8的方法,并且都是默认方法,有默认的实现。

2、Map.Entry

Map.Entry的定义如下:

interface Entry<K,V>

Map.Entry是Map中内部的一个接口,Map.Entry是键值对,Map通过entrySet()获取Map.Entry的键值对集合,从而通过该集合实现对键值对的操作。

interface Entry<K,V> {

	K getKey();

	V getValue();

	V setValue(V value);

	boolean equals(Object o);

	int hashCode();

	/**
	 * @since 1.8
	 */
	public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
		return (Comparator<Map.Entry<K, V>> & Serializable)
		(c1, c2) -> c1.getKey().compareTo(c2.getKey());
	}

	/**
	 * @since 1.8
	 */
	public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
		return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> c1.getValue().compareTo(c2.getValue());
	}

	/**
	 * @since 1.8
	 */
	public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
		Objects.requireNonNull(cmp);
		return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
	}

	/**
	 * @since 1.8
	 */
	public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
		Objects.requireNonNull(cmp);
		return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
	}
}

Entry接口中也添加了jdk 1.8的默认方法。

3、AbstractMap

AbstractMap的定义如下:

public abstract class AbstractMap<K,V> implements Map<K,V>

AbstractMap类提供Map接口的骨干实现,以最大限度地减少实现此接口所需的工作。
要实现不可修改的映射,编程人员只需扩展此类并提供entrySet方法的实现即可,该方法将返回映射的映射关系 set 视图。通常,返回的set将依次在AbstractSet上实现。此 set 不支持add()或remove()方法,其迭代器也不支持remove() 方法。

要实现可修改的映射,编程人员必须另外重写此类的put方法(否则将抛出UnsupportedOperationException),entrySet().iterator() 返回的迭代器也必须另外实现其remove方法。

protected AbstractMap() {
}

public int size() {
	return entrySet().size();
}

public boolean isEmpty() {
	return size() == 0;
}

public boolean containsValue(Object value) {
	Iterator<Entry<K,V>> i = entrySet().iterator();
	if (value==null) {
		while (i.hasNext()) {
			Entry<K,V> e = i.next();
			if (e.getValue()==null)
			return true;
		}
	} else {
		while (i.hasNext()) {
			Entry<K,V> e = i.next();
			if (value.equals(e.getValue()))
				return true;
		}
	}
	return false;
}

    public boolean containsKey(Object key) {
        Iterator<Map.Entry<K,V>> i = entrySet().iterator();
        if (key==null) {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (e.getKey()==null)
                    return true;
            }
        } else {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (key.equals(e.getKey()))
                    return true;
            }
        }
        return false;
    }

public V get(Object key) {
	Iterator<Entry<K,V>> i = entrySet().iterator();
	if (key==null) {
		while (i.hasNext()) {
			Entry<K,V> e = i.next();
			if (e.getKey()==null)
				return e.getValue();
		}
	} else {
		while (i.hasNext()) {
			Entry<K,V> e = i.next();
			if (key.equals(e.getKey()))
				return e.getValue();
		}
	}
	return null;
}

public V put(K key, V value) {
	throw new UnsupportedOperationException();
}

public V remove(Object key) {
	Iterator<Entry<K,V>> i = entrySet().iterator();
	Entry<K,V> correctEntry = null;
	if (key==null) {
		while (correctEntry==null && i.hasNext()) {
			Entry<K,V> e = i.next();
			if (e.getKey()==null)
			correctEntry = e;
		}
	} else {
		while (correctEntry==null && i.hasNext()) {
			Entry<K,V> e = i.next();
			if (key.equals(e.getKey()))
				correctEntry = e;
		}
	}

	V oldValue = null;
	if (correctEntry !=null) {
		oldValue = correctEntry.getValue();
		i.remove();
	}
	return oldValue;
}

public void putAll(Map<? extends K, ? extends V> m) {
	for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
		put(e.getKey(), e.getValue());
}

public void clear() {
	entrySet().clear();
}

transient Set<K>        keySet;
transient Collection<V> values;

public Set<K> keySet() {
	Set<K> ks = keySet;
	if (ks == null) {
		ks = new AbstractSet<K>() {
			public Iterator<K> iterator() {
				return new Iterator<K>() {
					private Iterator<Entry<K,V>> 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);
			}
		};
		keySet = ks;
	}
	return ks;
}

    public Collection<V> values() {
        Collection<V> vals = values;
        if (vals == null) {
            vals = new AbstractCollection<V>() {
                public Iterator<V> iterator() {
                    return new Iterator<V>() {
                        private Iterator<Entry<K,V>> 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);
                }
            };
            values = vals;
        }
        return vals;
    }

    public abstract Set<Entry<K,V>> entrySet();

    public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Map))
            return false;
        Map<?,?> m = (Map<?,?>) o;
        if (m.size() != size())
            return false;

        try {
            Iterator<Entry<K,V>> i = entrySet().iterator();
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value == null) {
                    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;
    }

    public int hashCode() {
        int h = 0;
        Iterator<Entry<K,V>> i = entrySet().iterator();
        while (i.hasNext())
            h += i.next().hashCode();
        return h;
    }

    public String toString() {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        if (! i.hasNext())
            return "{}";

        StringBuilder sb = new StringBuilder();
        sb.append('{');
        for (;;) {
            Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            sb.append(key   == this ? "(this Map)" : key);
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value);
            if (! i.hasNext())
                return sb.append('}').toString();
            sb.append(',').append(' ');
        }
    }

    protected Object clone() throws CloneNotSupportedException {
        AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
        result.keySet = null;
        result.values = null;
        return result;
    }

    private static boolean eq(Object o1, Object o2) {
        return o1 == null ? o2 == null : o1.equals(o2);
    }

    /**
     * @since 1.6
     */
    public static class SimpleEntry<K,V> implements Entry<K,V>, 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<? extends K, ? extends V> 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;
        }
    }

    /**
     * @since 1.6
     */
    public static class SimpleImmutableEntry<K,V> implements Entry<K,V>, java.io.Serializable {
        private static final long serialVersionUID = 7138329143949025153L;

        private final K key;
        private final V value;

        public SimpleImmutableEntry(K key, V value) {
            this.key   = key;
            this.value = value;
        }

        public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
            this.key   = entry.getKey();
            this.value = entry.getValue();
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        public V setValue(V value) {
            throw new UnsupportedOperationException();
        }

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

4、SortedMap

SortedMap的定义如下:

public interface SortedMap<K,V> extends Map<K,V>

SortedMap是一个继承于Map接口的接口。它是一个有序的SortedMap键值映射。
SortedMap的排序方式有两种:自然排序或者用户指定比较器。插入有序SortedMap的所有元素都必须实现Comparable接口(或者被指定的比较器所接受)。

另外,所有SortedMap 实现类都应该提供 4 个“标准”构造方法:
(1) void(无参数)构造方法,它创建一个空的有序映射,按照键的自然顺序进行排序。
(2) 带有一个Comparator类型参数的构造方法,它创建一个空的有序映射,根据指定的比较器进行排序。
(3) 带有一个Map类型参数的构造方法,它创建一个新的有序映射,其键-值映射关系与参数相同,按照键的自然顺序进行排序。
(4) 带有一个SortedMap类型参数的构造方法,它创建一个新的有序映射,其键-值映射关系和排序方法与输入的有序映射相同。无法保证强制实施此建议,因为接口不能包含构造方法。

Comparator<? super K> comparator();

SortedMap<K,V> subMap(K fromKey, K toKey);

SortedMap<K,V> headMap(K toKey);

SortedMap<K,V> tailMap(K fromKey);

K firstKey();

K lastKey();

Set<K> keySet();

Collection<V> values();

Set<Map.Entry<K, V>> entrySet();

5、NavigableMap

NavigableMap的定义如下:

public interface NavigableMap<K,V> extends SortedMap<K,V>

NavigableMap是继承于SortedMap的接口。它是一个可导航的键-值对集合,具有了为给定搜索目标报告最接近匹配项的导航方法。NavigableMap分别提供了获取“键”、“键-值对”、“键集”、“键-值对集”的相关方法。

Map.Entry<K,V> lowerEntry(K key);

K lowerKey(K key);

Map.Entry<K,V> floorEntry(K key);

K floorKey(K key);

Map.Entry<K,V> ceilingEntry(K key);

K ceilingKey(K key);

Map.Entry<K,V> higherEntry(K key);

K higherKey(K key);

Map.Entry<K,V> firstEntry();

Map.Entry<K,V> lastEntry();

Map.Entry<K,V> pollFirstEntry();

Map.Entry<K,V> pollLastEntry();

NavigableMap<K,V> descendingMap();

NavigableSet<K> navigableKeySet();

NavigableSet<K> descendingKeySet();

NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,K toKey,   boolean toInclusive);

NavigableMap<K,V> headMap(K toKey, boolean inclusive);

NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);

SortedMap<K,V> subMap(K fromKey, K toKey);

SortedMap<K,V> headMap(K toKey);

SortedMap<K,V> tailMap(K fromKey);

这里有几点需要特别说明:
NavigableMap除了继承SortedMap的特性外,它的提供的功能可以分为4类: 第1类,提供操作键-值对的方法。lowerEntry、floorEntry、ceilingEntry和higherEntry方法,它们分别返回与小于、小于等于、大于等于、大于给定键的键关联的Map.Entry对象。firstEntry、pollFirstEntry、lastEntry和pollLastEntry方法,它们返回和/或移除最小和最大的映射关系(如果存在),否则返回 null。
第2类,提供操作键的方法。这个和第1类比较类似。lowerKey、floorKey、ceilingKey和higherKey方法,它们分别返回与小于、小于等于、大于等于、大于给定键的键。
第3类,获取键集。navigableKeySet、descendingKeySet分别获取正序/反序的键集。
第4类,获取键-值对的子集。

6、Dictionary

Dictionary的定义如下:

public abstract class Dictionary<K,V>

Dictionary是JDK 1.0定义的键值对的抽象类,它也包括了操作键值对的基本函数。

public Dictionary() {
}

abstract public int size();

abstract public boolean isEmpty();

abstract public Enumeration<K> keys();

abstract public Enumeration<V> elements();

abstract public V get(Object key);

abstract public V put(K key, V value);

abstract public V remove(Object key);

四、参考

Java 集合系列09之 Map架构