Java-Map集合
介绍
HashMap是Map接口使用频率最高的实现类。
允许使用null
键和nul
l值,与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的默认容量,16MAXIMUM_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)
方法