简介
本文用实例介绍HashMap的操作,包括:方法大全、创建、排序(按照key排序和按照value排序)、按插入顺序存放、遍历方法及其性能、重写equels和hashCode。
方法大全
返回类型 | 方法和描述 | 示例 |
void | clear() 从该Map中删除所有的映射(可选操作)。 | |
default V | compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 通过指定键来进行处理。 若我们返回null,则在map中移除对应的key-value对。 | Map<String, Integer> map = new HashMap<>(); Integer compute = map.compute("cc", (k, v) -> (v == null) ? 0 : 2 * v); |
default V | computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 如果指定的键尚未与值相关联(或映射到 null ),则尝试使用给定的映射函数计算其值,并将其加入到此map中。 若我们返回null,则在map中移除对应的key-value对。 | Map<String, Integer> map = new HashMap<>(); Integer computeIfAbsent = map.computeIfAbsent("cc", k -> { |
default V | computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 如果指定的密钥的值存在且非空,则尝试计算给定密钥及其当前映射值的新映射。 若我们返回null,则在map中移除对应的key-value对。 | Map<String, Integer> map = new HashMap<>(); Integer computeIfPresent1 = map.computeIfPresent("bb", (k, v) -> { |
boolean | containsKey(Object key) 如果此映射包含指定键的映射,则返回 true 。 | |
boolean | containsValue(Object value) 如果此地图将一个或多个键映射到指定的值,则返回 true 。 | |
Set<Map. Entry<K,V>> | entrySet() 返回此地图中包含的映射的Set视图。 | |
boolean | equals(Object o) 将指定的对象与此映射进行比较以获得相等性。 | |
default void | forEach(BiConsumer<? super K,? super V> action) 对此映射中的每个条目执行给定的操作,直到所有条目都被处理或操作引发异常。 | |
V | get(Object key) 如果有该key:返回到指定键所映射的值。如果没有该key,返回null; | |
default V | getOrDefault(Object key, V defaultValue) 如果有该key:返回到指定键所映射的值。如果没有该key,返回defaultValue; | Map<String, Integer> map = new HashMap<>(); Integer getOrDefault = map.getOrDefault("cc", 0); |
int | hashCode() 返回此地图的哈希码值。 | |
boolean | isEmpty() 如果此地图不包含键值映射,则返回 true 。 | |
Set<K> | keySet() 返回此地图中包含的键的Set视图。 | |
default V | merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) 如果指定的键尚未与值相关联或与null相关联,则将其与给定的非空值相关联。 | |
V | put(K key, V value) 将指定的值与该映射中的指定键相关联(可选操作)。 若key已经有value,则覆盖。 | |
void | putAll(Map<? extends K,? extends V> m) 将指定map的所有映射复制到此映射(可选操作)。 | |
default V | putIfAbsent(K key, V value) 如果指定的键尚未与某个值相关联(或映射到 null )将其与给定值相关联并返回 null ,否则返回当前值。 | |
V | remove(Object key) 如果存在(从可选的操作),从该地图中删除一个键的映射。 | |
default boolean | remove(Object key, Object value) 仅当指定的密钥当前映射到指定的值时删除该条目。 | |
default V | replace(K key, V value) 只有当目标映射到某个值时,才能替换指定键的条目。 | |
default boolean | replace(K key, V oldValue, V newValue) 仅当当前映射到指定的值时,才能替换指定键的条目。 | |
default void | replaceAll(BiFunction<? super K,? super V,? extends V> function) 将每个条目的值替换为对该条目调用给定函数的结果,直到所有条目都被处理或该函数抛出异常。 | |
int | size() 返回此地图中键值映射的数量。 | |
Collection<V> | values() 返回此地图中包含的值的Collection视图。 |
Function<T, R> //只能接受一个参数
java/util/function/Function.java
package org.example.a;
import java.util.function.Function;
public class Demo {
public static void main(String[] args) {
Function<Integer, Integer> fun1= arg -> arg * arg;
Integer apply = fun1.apply(10);
System.out.println(apply); // 100
}
}
运行结果
100
BiFunction<T, U, R> //可接受两个参数
java/util/function/BiFunction.java
package org.example.a;
import java.util.function.BiFunction;
public class Demo {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> fun2 = (arg1, arg2) -> arg1 + arg2;
Integer sum = fun2.apply(10, 20);
System.out.println(sum); // 30
}
}
运行结果
30
创建
法1:匿名内部类
HashMap<String, String> map = new HashMap<String, String>() {{
put("name", "test");
put("age", "20");
}};
缺点
1.内存泄露隐患
非静态内部类/ 匿名内部类包含了外围实例的引用, 如果拥有比 外部类更长的生命周期,有内存泄露隐患。
2.如果这个对象要串行化,可能会导致串行化失败。
- 此种方式是匿名内部类的声明方式,所以引用中持有着外部类的引用。所以当时串行化这个集合时外部类也会被不知不觉的串行化,当外部类没有实现serialize接口时,就会报错。
- 上例中,其实是声明了一个继承自HashMap的子类。然而有些串行化方法,例如要通过Gson串行化为json,或者要串行化为xml时,类库中提供的方式,是无法串行化Hashset或者HashMap的子类的,从而导致串行化失败。
解决办法:重新初始化为一个HashMap对象:
HashMap<String, String> map = new HashMap<String, String>() {{
put("name", "test");
put("age", "20");
}};
HashMap<String, String> newMap = new HashMap(map);
这样就可以正常初始化了。
法2:静态方法
public class Demo{
private static final Map<String, String> myMap;
static {
myMap = new HashMap<String, String>();
myMap.put("a", "b");
myMap.put("c", "d");
}
}
法3:第三方包Guava
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
//或者
Map<String, String> test = ImmutableMap.<String, String>builder()
.put("k1", "v1")
.put("k2", "v2")
...
.build();
排序已有数据
按key排序
使用stream进行排序(按key升序/降序)
package org.example.a;
import java.util.*;
public class Demo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("ad", "dd");
map.put("bc", "ee");
map.put("cb", "ff");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
Map<String, String> linkedHashMap = new LinkedHashMap<>();
// 默认按照升序排列
map.entrySet().stream().sorted(Map.Entry.comparingByKey())
.forEach(o -> linkedHashMap.put(o.getKey(), o.getValue()));
for (Map.Entry<String, String> entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
Map<String, String> linkedHashMap1 = new LinkedHashMap<>();
// 自定义排序(降序)
map.entrySet().stream().sorted(Map.Entry.comparingByKey(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
})).forEach(o -> linkedHashMap1.put(o.getKey(), o.getValue()));
for (Map.Entry<String, String> entry : linkedHashMap1.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}
执行结果
bc:ee
ad:dd
cb:ff
ad:dd
bc:ee
cb:ff
cb:ff
bc:ee
ad:dd
HashMap转TreeMap自定义排序(按key升序/降序)
package org.example.a;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class Demo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("ad", "dd");
map.put("bc", "ee");
map.put("cb", "ff");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
// 默认按照升序排序
Map<String, String> map1 = new TreeMap<>();
map.forEach(map1::put);
for (Map.Entry<String, String> entry : map1.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
// 自定义排序(降序)
Map<String, String> map2 = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
map.forEach(map2::put);
for (Map.Entry<String, String> entry : map2.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}
执行结果
bc:ee
ad:dd
cb:ff
ad:dd
bc:ee
cb:ff
cb:ff
bc:ee
ad:dd
按value排序
使用stream进行排序(按value升序/降序)
package org.example.a;
import java.util.*;
public class Demo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("ad", "dd");
map.put("bc", "ee");
map.put("cb", "ff");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
Map<String, String> linkedHashMap = new LinkedHashMap<>();
// 默认按照升序排列
map.entrySet().stream().sorted(Map.Entry.comparingByValue())
.forEach(o -> linkedHashMap.put(o.getKey(), o.getValue()));
for (Map.Entry<String, String> entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
Map<String, String> linkedHashMap1 = new LinkedHashMap<>();
// 自定义排序(降序)
map.entrySet().stream().sorted(Map.Entry.comparingByValue(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
})).forEach(o -> linkedHashMap1.put(o.getKey(), o.getValue()));
for (Map.Entry<String, String> entry : linkedHashMap1.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}
执行结果
bc:ee
ad:dd
cb:ff
ad:dd
bc:ee
cb:ff
cb:ff
bc:ee
ad:dd
借助List进行排序(按value升序/降序)
原理:将待排序Map中的所有元素置于一个列表中,接着使用Collections的一个静态方法 sort(List<T> list, Comparator<? super T> c) 来排序列表,同样是用比较器定义比较规则。排序后的列表中的元素再依次装入Map,为了肯定的保证Map中元素与排序后的List中的元素的顺序一致,使用了LinkedHashMap数据类型。
本处只写升序代码,降序只是调换个顺序而已。
package org.example.a;
import java.util.*;
public class Demo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("ad", "dd");
map.put("bc", "ee");
map.put("cb", "ff");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
Map<String, String> sortedMap = new LinkedHashMap<>();
List<Map.Entry<String, String>> entryList = new ArrayList<Map.Entry<String, String>>(
map.entrySet());
Collections.sort(entryList, new Comparator<Map.Entry<String, String>>() {
@Override
public int compare(Map.Entry<String, String> me1, Map.Entry<String, String> me2) {
return me1.getValue().compareTo(me2.getValue());
}
});
for (Map.Entry<String, String> stringStringEntry : entryList) {
sortedMap.put(stringStringEntry.getKey(), stringStringEntry.getValue());
}
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}
运行结果
bc:ee
ad:dd
cb:ff
ad:dd
bc:ee
cb:ff
按插入顺序存放
使用HashMap
package org.example.a;
import java.util.*;
public class Demo{
public static List arrayList = new ArrayList();
public static void main(String[] args) {
Map<String, String> hashMap = new HashMap<String, String>();
hashMap.put("name1", "josan1");
hashMap.put("name2", "josan2");
hashMap.put("name3", "josan3");
Set<Map.Entry<String, String>> set = hashMap.entrySet();
Iterator<Map.Entry<String, String>> iterator = set.iterator();
while(iterator.hasNext()) {
Map.Entry entry = iterator.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
System.out.println("key:" + key + ", value:" + value);
}
}
}
执行结果(未按照插入顺序输出)
key:name3, value:josan3
key:name2, value:josan2
key:name1, value:josan1
使用LinkedHashMap
package org.example.a;
import java.util.*;
public class Demo{
public static List arrayList = new ArrayList();
public static void main(String[] args) {
Map<String, String> hashMap = new LinkedHashMap<String, String>();
hashMap.put("name1", "josan1");
hashMap.put("name2", "josan2");
hashMap.put("name3", "josan3");
Set<Map.Entry<String, String>> set = hashMap.entrySet();
Iterator<Map.Entry<String, String>> iterator = set.iterator();
while(iterator.hasNext()) {
Map.Entry entry = iterator.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
System.out.println("key:" + key + ", value:" + value);
}
}
}
执行结果(按照插入顺序输出)
key:name1, value:josan1
key:name2, value:josan2
key:name3, value:josan3
遍历方法及其性能
遍历方法
方法 | 说明/示例 |
for each map.entrySet() | Map<String, String> map = new HashMap<String, String>(); for (Entry<String, String> entry : map.entrySet()) { entry.getKey(); entry.getValue(); } |
调用map.entrySet()的集合迭代器 | Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { entry.getKey(); entry.getValue(); } |
for each map.keySet(),再调用get获取 | Map<String, String> map = new HashMap<String, String>(); for (String key : map.keySet()) { map.get(key); } |
遍历方法对比
三种遍历方式的性能测试及对比
测试环境:Windows7 32位系统 3.2G双核CPU 4G内存,Java 7,Eclipse -Xms512m -Xmx512m
测试结果:
map size | 10,000 | 100,000 | 1,000,000 | 2,000,000 |
for each entrySet | 2ms | 6ms | 36ms | 91ms |
for iterator entrySet | 0ms | 4ms | 35ms | 89ms |
for each keySet | 1ms | 6ms | 48ms | 126ms |
遍历方式结果分析(由上表可知):
- for each entrySet与for iterator entrySet性能等价
- for each keySet由于要再调用get(key)获取值,比较耗时(若hash散列算法较差,会更加耗时)
- 在循环过程中若要对map进行删除操作,只能用for iterator entrySet(在HahsMap非线程安全里介绍)。
HashMap entrySet源码
private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
return nextEntry();
}
}
HashMap keySet源码
private final class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
由源码可知:
keySet()与entrySet()都是返回set的迭代器。父类相同,只是返回值不同,因此性能差不多。只是keySet()多了一步根据key get value的操作而已。get的时间复杂度取决于for循环的次数,即hash算法。
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
/**
1. Returns the entry associated with the specified key in the
2. HashMap. Returns null if the HashMap contains no mapping
3. for the key.
*/
final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
使用场景总结
方法 | 使用场景 |
for each map.entrySet() | 循环中需要key、value,但不对map进行删除操作 |
调用map.entrySet()的集合迭代器 | 循环中需要key、value,且要对map进行删除操作 |
for each map.keySet() | 循环中只需要key |
重写equals和hashCode
如何编写出高质量的 equals 和 hashcode 方法? - 掘金
其他网址
Java HashMap三种循环遍历方式及其性能对比实例分析_java_脚本之家