0
点赞
收藏
分享

微信扫一扫

Java--HashMap--创建/排序/遍历/重写equels和hashCode

40dba2f2a596 2022-02-15 阅读 75


简介

        本文用实例介绍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<>();
map.put("aa", 1);
map.put("bb", 1);

Integer compute = map.compute("cc", (k, v) -> (v == null) ? 0 : 2 * v);
System.out.println(compute);  //0
System.out.println(map);    //{cc=0, aa=1, bb=1}



default ​​V​​



​​computeIfAbsent​​(​​K​​ key, ​​Function​​<? super ​​K​​,? extends ​​V​​> mappingFunction)

如果指定的键尚未与值相关联(或映射到 null ),则尝试使用给定的映射函数计算其值,并将其加入到此map中。

若我们返回null,则在map中移除对应的key-value对。



Map<String, Integer> map = new HashMap<>();
map.put("aa", 1);
map.put("bb", 1);

Integer computeIfAbsent = map.computeIfAbsent("cc", k -> {
    return 0;
});
System.out.println(computeIfAbsent);  //0
System.out.println(map);   //{cc=0, aa=1, bb=1}



default ​​V​​



​​computeIfPresent​​(​​K​​ key, ​​BiFunction​​<? super ​​K​​,? super ​​V​​,? extends ​​V​​> remappingFunction)

如果指定的密钥的值存在且非空,则尝试计算给定密钥及其当前映射值的新映射。

若我们返回null,则在map中移除对应的key-value对。



Map<String, Integer> map = new HashMap<>();
map.put("aa", 1);
map.put("bb", 1);

Integer computeIfPresent1 = map.computeIfPresent("bb", (k, v) -> {
    return v * 2;
});
System.out.println(computeIfPresent1);  //2
System.out.println(map);  //{aa=1, bb=2}



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<>();
map.put("aa", 1);
map.put("bb", 1);

Integer getOrDefault = map.getOrDefault("cc", 0);
System.out.println(getOrDefault);   //0
System.out.println(map);    //{aa=1, bb=1}



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.如果这个对象要串行化,可能会导致串行化失败。


  1. 此种方式是匿名内部类的声明方式,所以引用中持有着外部类的引用。所以当时串行化这个集合时外部类也会被不知不觉的串行化,当外部类没有实现serialize接口时,就会报错。
  2. 上例中,其实是声明了一个继承自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_脚本之家​​


举报

相关推荐

0 条评论