三、Map
先来讲一下Map,Map和Collection完全不是一个系列的,按理说讲完Collection的List,应该接着讲Collection的Set,但是因为Set中很多实现是基于Map来实现的,所以将Map提前。Map是一个接口,存储内容是键值对key-value,键不可重复。
1.HashMap
AbstractMap是实现Map接口的抽象类,HashMap继承于AbstractMap。
Map的API:(JDK1.8版本新增较多,为25个详见 https://docs.oracle.com/javase/8/docs/api/index.html)
abstract void clear() abstract boolean containsKey(Object key) abstract boolean containsValue(Object value) abstract Set<Entry<K, V>> entrySet() abstract boolean equals(Object object) abstract V get(Object key) abstract int hashCode() abstract boolean isEmpty() abstract Set<K> keySet() abstract V put(K key, V value) abstract void putAll(Map<? extends K, ? extends V> map) abstract V remove(Object key) abstract int size() abstract Collection<V> values()
AbstractMap的API只比Map新增了两个方法:
String toString() Object clone()
Map.Entry是Map类的静态接口,其API为 :
abstract boolean equals(Object object) abstract K getKey() abstract V getValue() abstract int hashCode() abstract V setValue(V object)
HashMap是线程不安全的,其key和value都可以是null。此外,HashMap不是有序的映射(什么是有序? 2019.1.1补:遍历的顺序和插入的顺序无直接关系)。
(1)HashMap成员域
HashMap基本成员域有:
transient Node<K,V>[] table;
transient Set<Map.Entry<K,V>> entrySet;
transient int size;
transient int modCount;
int threshold;
final float loadFactor;
table即为实现hashmap的核心,数组的每个元素是Node类型,本质上是一个链表,用于存储键值对。JDK7以下的table可以用下图来表示原理:
size是存储键值对的个数,loadfactor称为装载因子,默认为0.75,threshold=capacity*loadfactor,如果size大于阈值threshold就要进行扩容,而容量一般是2的幂数。因此loadfactor作用是为了使哈希中的存储更加均匀,减小冲突。显而易见的是,如果想节省存储空间,那么loadfactor应该设置的大一点(记住减少扩容就很好理解了),若想提高查询效率,loadfactor应该设置的大一点。
Note:桶的数量即为capacity。
(2)HashMap的构造函数
HashMap共有四种构造函数:
public HashMap()
public HashMap(int initialCapacity)
public HashMap(int initialCapacity,float loadFactor)
public HashMap(Map<? extends K,? extends V> m)
HashMap默认设置容量capacity为16,而装载因子loadfactor为0.75,也可以按照上面构造函数自定义。
详细的构造函数如下面的代码:
/** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and load factor. * * @param initialCapacity the initial capacity * @param loadFactor the load factor * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); } /** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and the default load factor (0.75). * * @param initialCapacity the initial capacity. * @throws IllegalArgumentException if the initial capacity is negative. */ public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } /** * Constructs a new <tt>HashMap</tt> with the same mappings as the * specified <tt>Map</tt>. The <tt>HashMap</tt> is created with * default load factor (0.75) and an initial capacity sufficient to * hold the mappings in the specified <tt>Map</tt>. * * @param m the map whose mappings are to be placed in this map * @throws NullPointerException if the specified map is null */ public HashMap(Map<? extends K, ? extends V> m) { this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); }