Java.util.HashMap,继承了AbstractMap,实现了Map、Clonable、Serializable接口。
它是以key-value键值对的形式存放的哈希表,其中key和value都允许null值。
两个重要参数
· 初始容量,默认为16,是哈希表中桶的数量,简单理解为其能存放的key-value键值对个数。
· 加载因子,默认为0.75,是表中已填充的记录数与总容量的比值,当数目达到这一比值时,将会自动扩容为两倍。
加载因子过高会提高空间利用率,但是降低了查找的性能;加载因子过低时可能会有频繁的扩容操作,同样影响性能甚至浪费存储空间。
所以在设置初始容量时应合理预估可能的数据条数以及总容量和加载因子的关系,尽量选用折中的数值。
构造函数
HashMap() 使用默认的初始容量16和加载因子0.75构造一个空的hashmap。
HashMap(int initialCapacity) 使用指定的初始容量initialCapacity和默认的加载因子0.75构造一个空的hashmap。
HashMap(int initialCapacity, float loadFactor) 使用指定的初始容量initialCapacity和指定的加载因子loadFactor构造一个空的hashmap。
HashMap(Map<? extends K, ? extends V> m) 构造与指定Map m相同映射的新HashMap。 HashMap使用默认加载因子0.75,初始容量由指定映射决定,足以将映射保存在指定的映射中。
常用方法
· 插入
V put(K key, V value) 把指定的key和value插入map中。如果map中已存在该key,则原value值将被替换成指定值。返回与该key匹配的原value值。由于hashmap支持value为null值,所以put函数可能返回null,并且有两种情况:在put执行之前,map中不存在指定的key值,或该key的原value值为null。
void putAll(Map<? extends K, ? extends V> m) 把指定的map中的映射都插入到当前map中。如果指定map中有key值已经存在于当前map,则进行value值替换。无返回值。
· 删除
V remove(Object key) 删除与给定key相关联的映射,返回其value值。同样存在两种返回null的情况:map中不存在指定的key值,或该key的原value值为null。
· 修改
boolean replace(K key, V oldValue, V newValue) 把指定的key-oldValue映射中oldValue值替换成newValue。这个操作用put(key, newValue)同样可以实现。
V replace(K key, V value) 只有当给定的key对应的value值不为null时,才将其替换成value。
· 查询
V get(Object key) 返回与给定key值相匹配的value值,不存在该key时返回null
boolean containsKey(Object key) 如果map中包含key值为给定值的映射,则返回true,否则返回false。
boolean containsValue(Object value) 如果map中包含value值为给定值的映射,则返回true,否则返回false。
在上述两种方法中,如果containsKey方法返回为true,则说明map中有且只有一个映射符合条件,如果containsValue返回true,则说明有至少一个,
因为key值不允许重复,而value值允许。
int size() 返回映射数量
boolean isEmpty() 如果map为空(size==0)则返回true,否则返回false
关于entrySet、keySet、values
final class EntrySet extends AbstractSet<Map.Entry<K,V>> 内部类继承了AbstractSet类,而AbstractSet实现了Set接口。它是包含了Map中映射关系的视图,依赖于map,对map进行键值修改会同步修改其entrySet,无需再次赋值。
Set<Map.Entry<K,V>> entrySet() 返回该视图
Map<String, Object> myHashMap = new HashMap<>();
myHashMap.put("key1", "value1");
myHashMap.put("key2", "value2");
Set<Map.Entry<String, Object>> entrySet = myHashMap.entrySet();
System.out.println(entrySet.toString());
myHashMap.put("key2", "value22");
System.out.println(entrySet.toString());
输出:
[key1=value1, key2=value2]
[key1=value1, key2=value22]<K>
final class KeySet extends AbstractSet<K>
内部类定义了包含map中所有key值的set集合。(map中key值不允许修改,会产生内存泄漏)
Set<K> keySet() 返回这个集合。
Set<String> keySet = myHashMap.keySet();
System.out.println(keySet.toString());
输出:
[key1, key2]
final class Values extends AbstractCollection<V> 内部类定义了包含map中所有value值的集合。由于key值不允许重复,所以key值集合采用了set,而value值允许重复,故为collection。
Collection<V> values()返回这个collection。
Collection values = myHashMap.values();
System.out.println(values.toString());
myHashMap.put("key1", "value111");
System.out.println(values.toString());
输出:
[value1, value22]
[value111, value22]
entry和迭代器iterator均可用来遍历,六种方法如下,其中value遍历的两种方法只能访问到value值,无法获得key值:
Map<String, String> myHashMap = new HashMap<>();
for(int i = 0; i < 3500000; i++){
myHashMap.put("key"+i, "value"+i);
}
//entrySet遍历
long startTime = System.currentTimeMillis();
Set<Map.Entry<String, String>> entrySet = myHashMap.entrySet();
for(Map.Entry<String, String> entry : entrySet){
entry.getKey();
entry.getValue();
}
System.out.println("entrySet遍历: " + (System.currentTimeMillis() - startTime));
//keySet遍历
startTime = System.currentTimeMillis();
Set<String> keySet = myHashMap.keySet();
for(String key: keySet){
myHashMap.get(key);
}
System.out.println("keySet遍历: " + (System.currentTimeMillis() - startTime));
//entry-iterator遍历
startTime = System.currentTimeMillis();
Iterator<Map.Entry<String, String>> entryIterator = myHashMap.entrySet().iterator();
while(entryIterator.hasNext()){
Map.Entry<String, String> entry = entryIterator.next();
entry.getKey();
entry.getValue();
}
System.out.println("entry-itereator遍历: " + (System.currentTimeMillis() - startTime));
//key-iterator遍历
startTime = System.currentTimeMillis();
Iterator<String> keyIterator = myHashMap.keySet().iterator();
while(keyIterator.hasNext()){
myHashMap.get(keyIterator.next());
}
System.out.println("key-itereator遍历: " + (System.currentTimeMillis() - startTime));
//value遍历
startTime = System.currentTimeMillis();
Collection<String> value = myHashMap.values();
for(String v : value){
;
}
System.out.println("value遍历: " + (System.currentTimeMillis() - startTime));
//value-iterator遍历
startTime = System.currentTimeMillis();
Iterator<String> valueIterator = myHashMap.values().iterator();
while(valueIterator.hasNext()){
valueIterator.next();
}
System.out.println("value-itereator遍历: " + (System.currentTimeMillis() - startTime));
输出:
entrySet遍历: 89
keySet遍历: 115
entry-itereator遍历: 89
key-itereator遍历: 118
value遍历: 96
value-itereator遍历: 83
整体来看,通过key去遍历的方法是效率最低的。这很容易想到,因为它在拿到key的集合或迭代器之后还要再做一次取值才能拿到value,而对于entry,直接就能拿到k-v键值对,无需再次从map中取值。value-iterator遍历效率最高,这仅适用于需要拿到value值而不关心key值的情况。