小伙伴们好呀,最近看到了 Java17 版本的信息以及 Spring6 基于 Java17 ……

感慨之余又想到这句,“你发任你发,我用 Java8” ,想了下,先来整理下 Java8 中 HashMap 新增的一些方法,看看自己平时工作中有用到多少~


Map 接口一览

这里 4ye 特意查看了下 1.7 和 1.8 版本的不同~ 结果发现1.8 版本多了这么的函数!(文章开始前,我列了下自己记得的。。也就 5 个🐷)

精通Java——Map篇_比较器

小插曲

这里简单介绍下 ComparableComparator 的区别 👇,后面会提到🐖

可以看到一个是 java.lang 包的,一个是 util 包的。

精通Java——Map篇_java_02

代码如下 👇

很明显,Comparable 属于 内部比较器, 而 Comparator 属于 外部比较器

外部比较器的好处 是我们可以有很多这种比较器,可以按排序的要求去选择 ,便于解耦。

而内部比较器也比较简单,只要实现了该 Comparable 接口就可以进行比较了。😝


class B implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
}
class C implements Comparable<Integer>{
private final int num;
public C(int num){
this.num=num;
}
@Override
public int compareTo(Integer o) {
return this.num-o;
}
}

Entry

那么,我们先从 内部类 Entry 开始叭~

从上面的图中,我们发现 1.8 中多了四个 comparing 开头的方法,而且返回值都是 Comparator 类型的。🐖


comparingByKey

public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}

一眼看过去,就觉得这个函数好复杂的样子😱
下面来一步步解析~ 🐖

  1. 从函数名可以得知它的主要作用应该是按照 key 自然排序
  2. 再仔细看看它,发现是一个泛型方法,主要说明 K 的上限是 Comparable (K必须实现这个接口)
  3. 再瞅瞅return语句中,居然有这么一个&符号(居然还能这么写😄), 猜测这里应该得同时实现 Comparator 和Serializable接口。
  4. 最后就一个常见的 lambda 表达式了,重写了 Comparator 中的 compare 方法
  5. 最后读读这个方法注释确认下

精通Java——Map篇_比较器_03

可以通过 stream 来使用 comparingByKey 进行排序。

List<Map.Entry<String, Integer>> collect = map.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(
Collectors.toList()
);

comparingByValue 也一样,这两个都是使用 内部比较器 Comparable

还有两个使用 外部比较器 Comparator 的例子,就不多介绍了。

精通Java——Map篇_ico_04

在代码中搜索了 1.8 后,发现总共有15个,除去上面 4 个 外,说明还有 11 个待介绍~ 😱

精通Java——Map篇_比较器_05

getOrDefault

精通Java——Map篇_java_06

可以发现 当这个 value 不为 null 时,或者 map 中有这个 key 时,就直接返回这个 value。

而不是说 value 不为 null 时才返回 value 的

tip:

使用时也得注意处理这个 null 的情况!!

例子

map.getOrDefault("Java4ye",2)

forEach

default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}

可以看到 forEach 中也是直接获取这个 entrySet() ,然后去处理每一个 key,value。
例子如下 👇

精通Java——Map篇_比较器_07

tip:

BiConsumerConsumer 接口类似,只不过它是接受 两个参数 ,而 Consumer 是一个。

认准 Bi 开头的,表示接受两个参数,其余效果和那 四大函数式接口一样。(下文就不多介绍了)🐖

精通Java——Map篇_java_08

replaceAll

default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}

// ise thrown from function is not a cme.
v = function.apply(k, v);

try {
entry.setValue(v);
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
}
}

这个方法的重点在 v = function.apply(k, v); 和 entry.setValue(v); 这两句上。
主要作用就是处理这个 value,然后再重新设置到 entry 中。

这里的 replaceAll 的含义应该是 replaceAllValue , 替换掉原先所有的 value。

例子 👇

private static void replaceAll(Map<String, Integer> map) {
map.replaceAll(
(k, v) -> {
if (v != null) {
v++;
} else {
v = 0;
}
return v;
}
);
}

精通Java——Map篇_比较器_09

putIfAbsent

default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}

return v;
}

这个就很简单了,如果值为 null 的话,就设置进去。

但是要注意它的返回值 是旧值。

remove

default boolean remove(Object key, Object value) {
Object curValue = get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
}

这里在原有 remove 方法的基础上,多增加了一层判断。
就是当当前值 curValue 和期望值 value 相等且 map 中存在这个 key 时才去删除

可以看到注释中有常规的做法 👇

精通Java——Map篇_比较器_10

replace

这两个也很简单,在 基础条件:有这个 key 的基础上,再多加一个条件,或者不加条件。和 remove 方法近似。


default boolean replace(K key, V oldValue, V newValue) {
Object curValue = get(key);
if (!Objects.equals(curValue, oldValue) ||
(curValue == null && !containsKey(key))) {
return false;
}
put(key, newValue);
return true;
}

当有这个 key 且 旧值等于期望值时才替换。精通Java——Map篇_比较器_11


default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}

这个是 key存在时就替换。🐷

不过我发现它们都喜欢返回 旧值 的,小伙伴们也要留意下~

精通Java——Map篇_ico_12

computeIfAbsent

default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}

return v;
}

当不存在时(指 key 不存在 或者 value 为 null 的情况),通过 Function 去获取这个 返回值,而且当这个 返回值不为 null 时,put 到 map 中。

精通Java——Map篇_比较器_13

computeIfPresent

default V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue;
if ((oldValue = get(key)) != null) {
V newValue = remappingFunction.apply(key, oldValue);
if (newValue != null) {
put(key, newValue);
return newValue;
} else {
remove(key);
return null;
}
} else {
return null;
}
}

存在时去算出这个新值,然后替换进去,和上面那个差不多。🐷

精通Java——Map篇_ico_14

例子👇

精通Java——Map篇_比较器_15

compute

default V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue = get(key);

V newValue = remappingFunction.apply(key, oldValue);
if (newValue == null) {
// delete mapping
if (oldValue != null || containsKey(key)) {
// something to remove
remove(key);
return null;
} else {
// nothing to do. Leave things as they were.
return null;
}
} else {
// add or replace old mapping
put(key, newValue);
return newValue;
}
}

这个是我目前看到的最复杂的一个了😂,看方法名都不知道他要干嘛

精通Java——Map篇_java_16

看了注释后发现它主要有下面两个功能 👇

1. 当 旧值不为 null 新值 为 null 时,删除该 key。

2. 新值不为 null 时,设置到 map 中。

小例子如下 👇 (重新计算对应 Key 的 value 值。)

map.compute(4L, (k, v) -> {
if (v == null) {
v = 1;
} else {
v++;
}
return v;
});

merge

default V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value :
remappingFunction.apply(oldValue, value);
if (newValue == null) {
remove(key);
} else {
put(key, newValue);
}
return newValue;
}

看过上面那个方法后,感觉这个居然清晰多了😄

1. 当 新值 为 null 时,删除该 key。

2. 当 旧值 为 null 时,新值 = value

3. 当 旧值 不为 null 时, 和 value 一起计算,得出 新值。

精通Java——Map篇_ico_17

本文涉及到的知识点如下 👇

精通Java——Map篇_ico_18

我是4ye 咱们下期应该……很快再见!! 😆

喜欢的话还可以关注下公众号 Java4ye 支持下 4ye 呀😝