小伙伴们好呀,最近看到了 Java17 版本的信息以及 Spring6 基于 Java17 ……
感慨之余又想到这句,“你发任你发,我用 Java8” ,想了下,先来整理下 Java8 中 HashMap 新增的一些方法,看看自己平时工作中有用到多少~
Map 接口一览
这里 4ye 特意查看了下 1.7 和 1.8 版本的不同~ 结果发现1.8 版本多了这么的函数!(文章开始前,我列了下自己记得的。。也就 5 个🐷)
小插曲
这里简单介绍下 Comparable 和 Comparator 的区别 👇,后面会提到🐖
可以看到一个是 java.lang 包的,一个是 util 包的。
代码如下 👇
很明显,Comparable 属于 内部比较器, 而 Comparator 属于 外部比较器 。
外部比较器的好处 是我们可以有很多这种比较器,可以按排序的要求去选择 ,便于解耦。
而内部比较器也比较简单,只要实现了该 Comparable 接口就可以进行比较了。😝
class B implements Comparator<Integer>{
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;
}
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());
}
一眼看过去,就觉得这个函数好复杂的样子😱
下面来一步步解析~ 🐖
- 从函数名可以得知它的主要作用应该是按照 key 自然排序
- 再仔细看看它,发现是一个泛型方法,主要说明 K 的上限是 Comparable (K必须实现这个接口)
- 再瞅瞅return语句中,居然有这么一个&符号(居然还能这么写😄), 猜测这里应该得同时实现 Comparator 和Serializable接口。
- 最后就一个常见的 lambda 表达式了,重写了 Comparator 中的 compare 方法
- 最后读读这个方法注释确认下
可以通过 stream 来使用 comparingByKey 进行排序。
List<Map.Entry<String, Integer>> collect = map.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(
Collectors.toList()
);
comparingByValue 也一样,这两个都是使用 内部比较器 Comparable 。
还有两个使用 外部比较器 Comparator 的例子,就不多介绍了。
在代码中搜索了 1.8 后,发现总共有15个,除去上面 4 个 外,说明还有 11 个待介绍~ 😱
getOrDefault
可以发现 当这个 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。
例子如下 👇
tip:
BiConsumer 和 Consumer 接口类似,只不过它是接受 两个参数 ,而 Consumer 是一个。
认准 Bi 开头的,表示接受两个参数,其余效果和那 四大函数式接口一样。(下文就不多介绍了)🐖
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;
}
);
}
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 时才去删除。
可以看到注释中有常规的做法 👇
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 且 旧值等于期望值时才替换。
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
这个是 key存在时就替换。🐷
不过我发现它们都喜欢返回 旧值 的,小伙伴们也要留意下~
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 中。
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;
}
}
存在时去算出这个新值,然后替换进去,和上面那个差不多。🐷
例子👇
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;
}
}
这个是我目前看到的最复杂的一个了😂,看方法名都不知道他要干嘛
看了注释后发现它主要有下面两个功能 👇
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 一起计算,得出 新值。
本文涉及到的知识点如下 👇
我是4ye 咱们下期应该……很快再见!! 😆
喜欢的话还可以关注下公众号 Java4ye 支持下 4ye 呀😝