介绍

HashMap是Map接口使用频率最高的实现类。

允许使用null键和null值,与HashSet一样,不保证映射的顺序。

所有的key是无序的、不可重复的。所以,key所在的类要重写equals()hashCode()

所有的value是无序的、可以重复的。所以,value所在的类要重写equals()

一个key-value构成一个entry,所有的entry是无序的、不可重复的。

HashMap判断两个key相等的标准是:两个key通过equals()方法返回true,hashCode值也相等。

HashMap判断两个value相等的标准是:两个value通过equals()方法返回true

常用方法

添加、删除、修改操作

方法

描述

Object put(Object key,Object value)

将指定key-value添加到(或修改)当前map对象中

void putAll(Map m)

将m中的所有key-value对存放到当前map中

Object remove(Object key)

移除指定key的key-value对,并返回value

void clear()

清空当前map中的所有数据

元素查询的操作

方法

描述

Object get(Object key)

获取指定key对应的value

boolean containsKey(Object key)

是否包含指定的key

boolean containsValue(Object value)

是否包含指定的value

int size()

返回map中key-value对的个数

boolean isEmpty()

判断当前map是否为空

boolean equals(Object obj)

判断当前map和参数对象obj是否相等

元视图操作的方法

方法

描述

Set keySet()

返回所有key构成的Set集合

Collection values()

返回所有value构成的Collection集合

Set entrySet()

返回所有key-value对构成的Set集合

HashMap

JDK7

底层存储Entry数组

/**
 * The table, resized as necessary. Length MUST Always be a power of two.
 */
transient Entry<K,V>[] table;

static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    Entry<K,V> next;
    int hash;

    /**
     * Creates new entry.
     */
    Entry(int h, K k, V v, Entry<K,V> n) {
        value = v;
        next = n;
        key = k;
        hash = h;
    }

    public final K getKey() {
        return key;
    }

    public final V getValue() {
        return value;
    }

    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }

    public final boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry e = (Map.Entry)o;
        Object k1 = getKey();
        Object k2 = e.getKey();
        if (k1 == k2 || (k1 != null && k1.equals(k2))) {
            Object v1 = getValue();
            Object v2 = e.getValue();
            if (v1 == v2 || (v1 != null && v1.equals(v2)))
                return true;
        }
        return false;
    }

    public final int hashCode() {
        return (key==null   ? 0 : key.hashCode()) ^
               (value==null ? 0 : value.hashCode());
    }

    public final String toString() {
        return getKey() + "=" + getValue();
    }

    /**
     * This method is invoked whenever the value in an entry is
     * overwritten by an invocation of put(k,v) for a key k that's already
     * in the HashMap.
     */
    void recordAccess(HashMap<K,V> m) {
    }

    /**
     * This method is invoked whenever the entry is
     * removed from the table.
     */
    void recordRemoval(HashMap<K,V> m) {
    }
}

构造器

创建了长度为16的Entry数组

其中,构造器中调用了init()方法,这个方法在HashMap的实现中并无意义,只是提供给子类(比如LinkedHashMap)实现相关的初始化调用。

扩容

默认情况下,扩容为原来的2倍

JDK8

底层存储Node数组

 /**
  * The table, initialized on first use, and resized as
  * necessary. When allocated, length is always a power of two.
  * (We also tolerate length zero in some operations to allow
  * bootstrapping mechanics that are currently not needed.)
  */
 transient Node<K,V>[] table;

构造器

空参构造器只是做了加载因子的赋值,并没有真正的创建数组

 /**
  * 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
 }

扩容

默认情况下,扩容为原来的2倍


HashMap源码中的重要常量

  • DEFAULT_INITIAL_CAPACITY:HashMap的默认容量,16

  • MAXIMUM_CAPACITY:HashMap的最大支持容量,

  • DEFAULT_LOAD_FACTOR:HashMap的默认加载因子

  • TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树

  • UNTREEIFY_THRESHOLD:Bucket中红黑树存储的Node小于该默认值,转化为链表

  • MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量。(当桶中Node的数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4倍)

  • table:存储元素的数组,总是2的n次幂

  • entrySet:存储具体元素的集

  • size:HashMap中存储的键值对的数量

  • modCount:HashMap扩容和结构改变的次数。

  • threshold:扩容的临界值,等于容量*填充因子

  • loadFactor:填充因子

JDK7和JDK8性能比较

put操作

  • hash比较均匀的时候(负载因子是0.75导致的)

次数

10

100

1000

10000

100000

JDK1.7时间(ns)

1100

720

832

914

912

JDK1.8时间(ns)

1019

1023

1188

267

115

  • hash不均匀的时候

次数

10

100

1000

10000

100000

JDK1.7时间(ns)

2500

14310

8151

14137

154319

JDK1.8时间(ns)

3765

38144

60707

1182

373

get操作

  • hash比较均匀的时候(负载因子是0.75导致的)

次数

10

100

1000

10000

100000

JDK1.7时间(ns)

900

550

627

302

626

JDK1.8时间(ns)

2773

1047

318

94

13

  • hash不均匀的时候

次数

10

100

1000

10000

100000

JDK1.7时间(ns)

2000

14950

4294

2167

16447

JDK1.8时间(ns)

3430

3932

2028

767

19

TreeMap

TreeMap存储Key-Value对时,需要根据key-value对进行排序。TreeMap可以保证所有的Key-Value对处于有序状态。

TreeSet底层使用红黑树结构存储数据。

TreeMap的Key的排序:

  • 自然排序:TreeMap的所有的Key必须实现Comparable接口,而且所有的Key应该是同一个类的对象,否则将会抛出ClasssCastException

  • 定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。此时不需要Map的Key实现Comparable接口

TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0

Properties

Properties类是Hashtable的子类,该对象用于处理属性文件。

由于属性文件里的key、value都是字符串类型,所以Properties里的key和value都是字符串类型。

存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法