原文网址:Java--HashMap--创建/排序/遍历/重写equels和hashCode_IT利刃出鞘的博客-CSDN博客
简介本文用实例介绍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 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 Integer computeIfAbsent = map.computeIfAbsent("cc", k -> { |
| default V | computeIfPresent(K key, BiFunction super K,? super V,? extends V> remappingFunction) 如果指定的密钥的值存在且非空,则尝试计算给定密钥及其当前映射值的新映射。 若我们返回null,则在map中移除对应的key-value对。 | Map Integer computeIfPresent1 = map.computeIfPresent("bb", (k, v) -> { |
| boolean | containsKey(Object key) 如果此映射包含指定键的映射,则返回 true 。 | |
| boolean | containsValue(Object value) 如果此地图将一个或多个键映射到指定的值,则返回 true 。 | |
| Set Entry | 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 Integer getOrDefault = map.getOrDefault("cc", 0); |
| int | hashCode() 返回此地图的哈希码值。 | |
| boolean | isEmpty() 如果此地图不包含键值映射,则返回 true 。 | |
| Set | 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 | values() 返回此地图中包含的值的Collection视图。 |
Function
java/util/function/Function.java
package org.example.a;
import java.util.function.Function;
public class Demo {
public static void main(String[] args) {
Function fun1= arg -> arg * arg;
Integer apply = fun1.apply(10);
System.out.println(apply); // 100
}
}
运行结果
100
BiFunction
java/util/function/BiFunction.java
package org.example.a;
import java.util.function.BiFunction;
public class Demo {
public static void main(String[] args) {
BiFunction fun2 = (arg1, arg2) -> arg1 + arg2;
Integer sum = fun2.apply(10, 20);
System.out.println(sum); // 30
}
}
运行结果
30创建
法1:匿名内部类
HashMapmap = new HashMap () {{ put("name", "test"); put("age", "20"); }};
缺点
1.内存泄露隐患
非静态内部类/ 匿名内部类包含了外围实例的引用, 如果拥有比 外部类更长的生命周期,有内存泄露隐患。
2.如果这个对象要串行化,可能会导致串行化失败。
- 此种方式是匿名内部类的声明方式,所以引用中持有着外部类的引用。所以当时串行化这个集合时外部类也会被不知不觉的串行化,当外部类没有实现serialize接口时,就会报错。
- 上例中,其实是声明了一个继承自HashMap的子类。然而有些串行化方法,例如要通过Gson串行化为json,或者要串行化为xml时,类库中提供的方式,是无法串行化Hashset或者HashMap的子类的,从而导致串行化失败。
解决办法:重新初始化为一个HashMap对象:
HashMapmap = new HashMap () {{ put("name", "test"); put("age", "20"); }}; HashMap newMap = new HashMap(map);
这样就可以正常初始化了。
法2:静态方法
public class Demo{
private static final Map myMap;
static {
myMap = new HashMap();
myMap.put("a", "b");
myMap.put("c", "d");
}
}
法3:第三方包Guava
Map排序已有数据 按key排序left = ImmutableMap.of("a", 1, "b", 2, "c", 3); //或者 Map test = ImmutableMap. builder() .put("k1", "v1") .put("k2", "v2") ... .build();
使用stream进行排序(按key升序/降序)
package org.example.a;
import java.util.*;
public class Demo {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("ad", "dd");
map.put("bc", "ee");
map.put("cb", "ff");
for (Map.Entry entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
Map linkedHashMap = new linkedHashMap<>();
// 默认按照升序排列
map.entrySet().stream().sorted(Map.Entry.comparingByKey())
.forEach(o -> linkedHashMap.put(o.getKey(), o.getValue()));
for (Map.Entry entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
Map linkedHashMap1 = new linkedHashMap<>();
// 自定义排序(降序)
map.entrySet().stream().sorted(Map.Entry.comparingByKey(new Comparator() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
})).forEach(o -> linkedHashMap1.put(o.getKey(), o.getValue()));
for (Map.Entry 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 map = new HashMap<>();
map.put("ad", "dd");
map.put("bc", "ee");
map.put("cb", "ff");
for (Map.Entry entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
// 默认按照升序排序
Map map1 = new TreeMap<>();
map.forEach(map1::put);
for (Map.Entry entry : map1.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
// 自定义排序(降序)
Map map2 = new TreeMap<>(new Comparator() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
map.forEach(map2::put);
for (Map.Entry 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 map = new HashMap<>();
map.put("ad", "dd");
map.put("bc", "ee");
map.put("cb", "ff");
for (Map.Entry entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
Map linkedHashMap = new linkedHashMap<>();
// 默认按照升序排列
map.entrySet().stream().sorted(Map.Entry.comparingByValue())
.forEach(o -> linkedHashMap.put(o.getKey(), o.getValue()));
for (Map.Entry entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
Map linkedHashMap1 = new linkedHashMap<>();
// 自定义排序(降序)
map.entrySet().stream().sorted(Map.Entry.comparingByValue(new Comparator() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
})).forEach(o -> linkedHashMap1.put(o.getKey(), o.getValue()));
for (Map.Entry 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
本处只写升序代码,降序只是调换个顺序而已。
package org.example.a;
import java.util.*;
public class Demo {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("ad", "dd");
map.put("bc", "ee");
map.put("cb", "ff");
for (Map.Entry entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println();
Map sortedMap = new linkedHashMap<>();
List> entryList = new ArrayList>(
map.entrySet());
Collections.sort(entryList, new Comparator>() {
@Override
public int compare(Map.Entry me1, Map.Entry me2) {
return me1.getValue().compareTo(me2.getValue());
}
});
for (Map.Entry stringStringEntry : entryList) {
sortedMap.put(stringStringEntry.getKey(), stringStringEntry.getValue());
}
for (Map.Entry 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 hashMap = new HashMap();
hashMap.put("name1", "josan1");
hashMap.put("name2", "josan2");
hashMap.put("name3", "josan3");
Set> set = hashMap.entrySet();
Iterator> 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 hashMap = new linkedHashMap();
hashMap.put("name1", "josan1");
hashMap.put("name2", "josan2");
hashMap.put("name3", "josan3");
Set> set = hashMap.entrySet();
Iterator> 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 for (Entry entry.getKey(); entry.getValue(); } |
| 调用map.entrySet()的集合迭代器 | Iterator while (iterator.hasNext()) { entry.getKey(); entry.getValue(); } |
| for each map.keySet(),再调用get获取 | Map 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> { public Map.Entry next() { return nextEntry(); } }
HashMap keySet源码
private final class KeyIterator extends HashIterator{ 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 entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
final Entry getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key);
for (Entry 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 |
其他网址
如何重写hashCode()和equals()方法_王鸿飞的专栏-CSDN博客
如何编写出高质量的 equals 和 hashcode 方法? - 掘金
Java HashMap 初始化赋值 (不建议)_AlbenXie的博客-CSDN博客_hashmap 初始化
JAVA构造MAP并初始化MAP_dujianxiong的博客-CSDN博客_java map初始化
java8 map根据key排序和根据value排序_weixin_40841731的博客-CSDN博客_map根据key排序
Java Map 按Key排序和按Value排序_Ricky-CSDN博客_java map key 排序
Java HashMap三种循环遍历方式及其性能对比实例分析_java_脚本之家
Java迭代器(转)(iterator详解以及和for循环的区别) - redcoatjk - 博客园



