From e0e33766e9767fd0cdebfd2de1325c3f17f7b762 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Mon, 20 Oct 2025 16:23:12 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20`MapModifier`?= =?UTF-8?q?=20=E7=B1=BB=EF=BC=8C=E6=94=AF=E6=8C=81=E9=93=BE=E5=BC=8F?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E4=BF=AE=E6=94=B9=20Map=20=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 MapModifier 类,提供了一系列对 Map 数据的修改操作方法,如 put、putIfAbsent、putAll、remove、clear 等 - 支持直接修改指定的 Map - 支持从 Supplier 中获取 Map 并进行修改 - 提供了创建不可修改 Map 的方法 - 增加了相应的单元测试用例 --- .../commons/collection/MapModifier.java | 221 ++++++++++++++++++ .../commons/collection/MapModifierTests.java | 118 ++++++++++ 2 files changed, 339 insertions(+) create mode 100644 plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java create mode 100644 plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/collection/MapModifierTests.java diff --git a/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java new file mode 100644 index 0000000..a0dfa74 --- /dev/null +++ b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java @@ -0,0 +1,221 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.zhouxy.plusone.commons.collection; + +import static xyz.zhouxy.plusone.commons.util.AssertTools.checkArgumentNotNull; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; + +/** + * Map 修改器 + * + *

+ * 封装一系列对 Map 数据的修改操作,修改 Map 的数据。可以用于 Map 的数据初始化等操作。 + * + *

+ * // MapModifier
+ * MapModifier modifier = new MapModifier()
+ *     .putAll(commonProperties)
+ *     .put("username", "Ben")
+ *     .put("accountStatus", LOCKED);
+ *
+ * // 从 Supplier 中获取 Map,并修改数据
+ * Map map = modifier.getAndModify(HashMap::new);
+ *
+ * // 可以灵活使用不同 Map 类型的不同构造器
+ * Map map = modifier.getAndModify(() -> new HashMap<>(8));
+ * Map map = modifier.getAndModify(() -> new HashMap<>(anotherMap));
+ * Map map = modifier.getAndModify(TreeMap::new);
+ * Map map = modifier.getAndModify(ConcurrentHashMap::new);
+ *
+ * // 修改已有的 Map
+ * modifier.modify(map);
+ *
+ * // 创建一个有初始化数据的不可变的 Map
+ * Map map = modifier.getUnmodifiableMap();
+ *
+ * // 链式调用创建并初始化数据
+ * Map map = new MapModifier()
+ *     .putAll(commonProperties)
+ *     .put("username", "Ben")
+ *     .put("accountStatus", LOCKED)
+ *     .getAndModify(HashMap::new);
+ * 
+ * + * @author ZhouXY108 + * @since 1.1.0 + */ +@Beta +public class MapModifier { + + @Nonnull + private Consumer> operators; + + /** + * 创建一个空的 MapModifier + */ + public MapModifier() { + this.operators = m -> { + // do nothing + }; + } + + /** + * 添加一个键值对 + * + * @param key 要添加的 {@code key} + * @param value 要添加的 {@code value} + * @return MapModifier + */ + public MapModifier put(K key, V value) { + return addOperationInternal(map -> map.put(key, value)); + } + + /** + * 添加一个键值对,如果 key 已经存在,则不添加 + * + * @param key 要添加的 {@code key} + * @param value 要添加的 {@code value} + * @return MapModifier + */ + public MapModifier putIfAbsent(K key, V value) { + return addOperationInternal(map -> map.putIfAbsent(key, value)); + } + + /** + * 添加多个键值对 + * + * @param otherMap 要添加的键值对集合 + * @return MapModifier + */ + public MapModifier putAll(Map otherMap) { + return addOperationInternal(map -> map.putAll(otherMap)); + } + + /** + * 添加多个键值对 + * + * @param entries 要添加的键值对集合 + * @return MapModifier + */ + @SafeVarargs + public final MapModifier putAll(Map.Entry... entries) { + return addOperationInternal(map -> { + for (Map.Entry entry : entries) { + map.put(entry.getKey(), entry.getValue()); + } + }); + } + + /** + * 当 {@code key} 不存在时,计算对应的值,并添加到 {@code map} 中 + * 调用 {@link Map#computeIfAbsent(Object, Function)} + * + * @param key 要添加的 {@code key} + * @param mappingFunction 计算 {@code key} 对应的值 + * @return MapModifier + */ + public MapModifier computeIfAbsent(K key, + Function mappingFunction) { + return addOperationInternal(map -> map.computeIfAbsent(key, mappingFunction)); + } + + /** + * 当 {@code key} 存在时,计算对应的值,并添加到 {@code map} 中 + * 调用 {@link Map#computeIfPresent(Object, BiFunction)} + * + * @param key 要添加的 {@code key} + * @param remappingFunction 计算 {@code key} 对应的值 + * @return MapModifier + */ + public MapModifier computeIfPresent(K key, + BiFunction remappingFunction) { + return addOperationInternal(map -> map.computeIfPresent(key, remappingFunction)); + } + + /** + * 删除 {@code key} + * + * @param key 要删除的 {@code key} + * @return MapModifier + */ + public MapModifier remove(K key) { + return addOperationInternal(map -> map.remove(key)); + } + + /** + * 清空 {@code map} + * + * @return MapModifier + */ + public MapModifier clear() { + return addOperationInternal(Map::clear); + } + + /** + * 修改 {@code map} + * + * @param map 要修改的 {@code map} + * @return 修改后的 {@code map}。当入参是 {@code null} 时,返回 {@code null}。 + */ + public > void modify(@Nullable T map) { + if (map != null) { + this.operators.accept(map); + } + } + + /** + * 修改 {@code map} + * + * @param mapSupplier {@code map} 的 {@link Supplier} + * @return 修改后的 {@code map}。 + * 当从 {@code mapSupplier} 获取的 {@code map} 为 {@code null} 时,返回 {@code null}。 + */ + @CheckForNull + public > T getAndModify(Supplier mapSupplier) { + checkArgumentNotNull(mapSupplier, "The map supplier cannot be null."); + T map = mapSupplier.get(); + modify(map); + return map; + } + + /** + * 创建一个有初始化数据的不可变的 {@code Map} + * + * @return 不可变的 {@code Map} + */ + public Map getUnmodifiableMap() { + return Collections.unmodifiableMap(getAndModify(HashMap::new)); + } + + private MapModifier addOperationInternal(Consumer> operator) { + this.operators = this.operators.andThen(operator); + return this; + } +} diff --git a/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/collection/MapModifierTests.java b/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/collection/MapModifierTests.java new file mode 100644 index 0000000..d1117d3 --- /dev/null +++ b/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/collection/MapModifierTests.java @@ -0,0 +1,118 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.zhouxy.plusone.commons.collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import org.junit.jupiter.api.Test; + +import com.google.common.collect.ImmutableMap; + +import lombok.extern.slf4j.Slf4j; + +// TODO 完善单元测试 +@Slf4j +public class MapModifierTests { + + private static final String APP_START_ID = UUID.randomUUID().toString(); + private static final String LOCKED = "LOCKED"; + + private static final Map commonProperties = ImmutableMap.builder() + .put("channel", "MOBILE") + .put("appStartId", APP_START_ID) + .build(); + + @Test + void initMap() { + Map expected = new HashMap() { + { + put("channel", "MOBILE"); + put("appStartId", APP_START_ID); + put("username", "Ben"); + put("accountStatus", LOCKED); + } + }; + + // MapModifier + MapModifier modifier = new MapModifier() + .putAll(commonProperties) + .put("username", "Ben") + .put("accountStatus", LOCKED); + + // 从 Supplier 中获取 Map,并修改数据 + HashMap hashMap1 = modifier.getAndModify(HashMap::new); + assertEquals(expected, hashMap1); + + // 可以灵活使用不同 Map 类型的不同构造器 + HashMap hashMap2 = modifier.getAndModify(() -> new HashMap<>(8)); + assertEquals(expected, hashMap2); + + // HashMap hashMap3 = modifier.getAndModify(() -> new HashMap<>(anotherMap)); + TreeMap treeMap = modifier.getAndModify(TreeMap::new); + assertEquals(expected, treeMap); + ConcurrentHashMap concurrentHashMap = modifier.getAndModify(ConcurrentHashMap::new); + assertEquals(expected, concurrentHashMap); + + // 修改已有的 Map + Map srcMap = new HashMap<>(); + srcMap.put("srcKey1", "srcValue1"); + srcMap.put("srcKey2", "srcValue2"); + modifier.modify(srcMap); + assertEquals(new HashMap() { + { + putAll(commonProperties); + put("username", "Ben"); + put("accountStatus", LOCKED); + put("srcKey1", "srcValue1"); + put("srcKey2", "srcValue2"); + } + }, srcMap); + + // 创建一个有初始化数据的不可变的 {@code Map} + Map unmodifiableMap = modifier.getUnmodifiableMap(); + assertEquals(expected, unmodifiableMap); + assertThrows(UnsupportedOperationException.class, + () -> unmodifiableMap.put("key", "value")); + } + + @Test + void createAndInitData() { + // 链式调用创建并初始化数据 + HashMap map = new MapModifier() + .putAll(commonProperties) + .put("username", "Ben") + .put("accountStatus", LOCKED) + .getAndModify(HashMap::new); + + HashMap expected = new HashMap() { + { + put("channel", "MOBILE"); + put("appStartId", APP_START_ID); + put("username", "Ben"); + put("accountStatus", LOCKED); + } + }; + assertEquals(expected, map); + } +} -- 2.49.1 From 3155964b7cb76a9aceecfbb9eaaed6f77c046113 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Mon, 20 Oct 2025 16:36:04 +0800 Subject: [PATCH 2/4] =?UTF-8?q?docs:=20=E4=BF=AE=E6=94=B9=20`MapModifier`?= =?UTF-8?q?=20javadoc=20=E4=B8=AD=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plusone/commons/collection/MapModifier.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java index a0dfa74..7266b71 100644 --- a/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java +++ b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java @@ -40,28 +40,28 @@ import com.google.common.annotations.Beta; * *
  * // MapModifier
- * MapModifier modifier = new MapModifier()
+ * MapModifier<String, Object> modifier = new MapModifier<String, Object>()
  *     .putAll(commonProperties)
  *     .put("username", "Ben")
  *     .put("accountStatus", LOCKED);
  *
  * // 从 Supplier 中获取 Map,并修改数据
- * Map map = modifier.getAndModify(HashMap::new);
+ * Map<String, Object> map = modifier.getAndModify(HashMap::new);
  *
  * // 可以灵活使用不同 Map 类型的不同构造器
- * Map map = modifier.getAndModify(() -> new HashMap<>(8));
- * Map map = modifier.getAndModify(() -> new HashMap<>(anotherMap));
- * Map map = modifier.getAndModify(TreeMap::new);
- * Map map = modifier.getAndModify(ConcurrentHashMap::new);
+ * Map<String, Object> map = modifier.getAndModify(() -> new HashMap<>(8));
+ * Map<String, Object> map = modifier.getAndModify(() -> new HashMap<>(anotherMap));
+ * Map<String, Object> map = modifier.getAndModify(TreeMap::new);
+ * Map<String, Object> map = modifier.getAndModify(ConcurrentHashMap::new);
  *
  * // 修改已有的 Map
  * modifier.modify(map);
  *
  * // 创建一个有初始化数据的不可变的 Map
- * Map map = modifier.getUnmodifiableMap();
+ * Map<String, Object> map = modifier.getUnmodifiableMap();
  *
  * // 链式调用创建并初始化数据
- * Map map = new MapModifier()
+ * Map<String, Object> map = new MapModifier<String, Object>()
  *     .putAll(commonProperties)
  *     .put("username", "Ben")
  *     .put("accountStatus", LOCKED)
-- 
2.49.1


From ddaacf1576a750e055d3d47debc6674f5f6008c3 Mon Sep 17 00:00:00 2001
From: ZhouXY108 
Date: Tue, 21 Oct 2025 14:39:46 +0800
Subject: [PATCH 3/4] =?UTF-8?q?docs:=20=E8=A1=A5=E5=85=85=20`MapModifier`?=
 =?UTF-8?q?=20=E7=9A=84=20javadoc?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../commons/collection/MapModifier.java       | 59 +++++++++++++++----
 1 file changed, 46 insertions(+), 13 deletions(-)

diff --git a/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java
index 7266b71..5cf5e97 100644
--- a/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java
+++ b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/collection/MapModifier.java
@@ -87,45 +87,65 @@ public class MapModifier {
     }
 
     /**
-     * 添加一个键值对
+     * 添加一个键值对。
+     *
+     * 

+ * 注意:键值对是否允许为 {@code null},由最终作用的 {@link Map} 类型决定。 * * @param key 要添加的 {@code key} * @param value 要添加的 {@code value} * @return MapModifier */ - public MapModifier put(K key, V value) { + public MapModifier put(@Nullable K key, @Nullable V value) { return addOperationInternal(map -> map.put(key, value)); } /** - * 添加一个键值对,如果 key 已经存在,则不添加 + * 添加一个键值对,如果 key 已经存在,则不添加。 + * + *

+ * 注意:键值对是否允许为 {@code null},由最终作用的 {@link Map} 类型决定。 * * @param key 要添加的 {@code key} * @param value 要添加的 {@code value} * @return MapModifier */ - public MapModifier putIfAbsent(K key, V value) { + public MapModifier putIfAbsent(@Nullable K key, @Nullable V value) { return addOperationInternal(map -> map.putIfAbsent(key, value)); } /** - * 添加多个键值对 + * 添加多个键值对。 + * + *

+ * 注意:键值对是否允许为 {@code null},由最终作用的 {@link Map} 类型决定。 + * + * @param otherMap 要添加的键值对集合。 + * 如果为 {@code null},则什么都不做。 * - * @param otherMap 要添加的键值对集合 * @return MapModifier */ - public MapModifier putAll(Map otherMap) { + public MapModifier putAll(@Nullable Map otherMap) { + if (otherMap == null || otherMap.isEmpty()) { + return this; + } return addOperationInternal(map -> map.putAll(otherMap)); } /** - * 添加多个键值对 + * 添加多个键值对。 + * + *

+ * 注意:键值对是否允许为 {@code null},由最终作用的 {@link Map} 类型决定。 * * @param entries 要添加的键值对集合 * @return MapModifier */ @SafeVarargs public final MapModifier putAll(Map.Entry... entries) { + if (entries.length == 0) { + return this; + } return addOperationInternal(map -> { for (Map.Entry entry : entries) { map.put(entry.getKey(), entry.getValue()); @@ -134,8 +154,13 @@ public class MapModifier { } /** - * 当 {@code key} 不存在时,计算对应的值,并添加到 {@code map} 中 - * 调用 {@link Map#computeIfAbsent(Object, Function)} + * 当 {@code key} 不存在时,计算对应的值,并添加到 {@code map} 中。 + * + *

+ * 调用 {@link Map#computeIfAbsent(Object, Function)}。 + * + *

+ * 注意:键值对是否允许为 {@code null},由最终作用的 {@link Map} 类型决定。 * * @param key 要添加的 {@code key} * @param mappingFunction 计算 {@code key} 对应的值 @@ -147,8 +172,13 @@ public class MapModifier { } /** - * 当 {@code key} 存在时,计算对应的值,并添加到 {@code map} 中 - * 调用 {@link Map#computeIfPresent(Object, BiFunction)} + * 当 {@code key} 存在时,计算对应的值,并添加到 {@code map} 中。 + * + *

+ * 调用 {@link Map#computeIfPresent(Object, BiFunction)}。 + * + *

+ * 注意:键值对是否允许为 {@code null},由最终作用的 {@link Map} 类型决定。 * * @param key 要添加的 {@code key} * @param remappingFunction 计算 {@code key} 对应的值 @@ -160,7 +190,10 @@ public class MapModifier { } /** - * 删除 {@code key} + * 删除 {@code key}。 + * + *

+ * 注意:key 是否允许为 {@code null},由最终作用的 {@link Map} 类型决定。 * * @param key 要删除的 {@code key} * @return MapModifier -- 2.49.1 From 8afe83e8df8cd2737af3463a04ce970f5ec173a7 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Tue, 21 Oct 2025 14:40:53 +0800 Subject: [PATCH 4/4] =?UTF-8?q?test:=20=E5=AE=8C=E5=96=84=20`MapModifier`?= =?UTF-8?q?=20=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commons/collection/MapModifierTests.java | 228 ++++++++++++++++-- 1 file changed, 213 insertions(+), 15 deletions(-) diff --git a/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/collection/MapModifierTests.java b/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/collection/MapModifierTests.java index d1117d3..d372d27 100644 --- a/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/collection/MapModifierTests.java +++ b/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/collection/MapModifierTests.java @@ -16,36 +16,42 @@ package xyz.zhouxy.plusone.commons.collection; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nullable; + import org.junit.jupiter.api.Test; import com.google.common.collect.ImmutableMap; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; -// TODO 完善单元测试 @Slf4j public class MapModifierTests { private static final String APP_START_ID = UUID.randomUUID().toString(); private static final String LOCKED = "LOCKED"; - private static final Map commonProperties = ImmutableMap.builder() + private static final Map commonProperties = ImmutableMap.builder() .put("channel", "MOBILE") .put("appStartId", APP_START_ID) .build(); @Test - void initMap() { - Map expected = new HashMap() { + void demo() { + Map expected = new HashMap() { { put("channel", "MOBILE"); put("appStartId", APP_START_ID); @@ -55,31 +61,33 @@ public class MapModifierTests { }; // MapModifier - MapModifier modifier = new MapModifier() + MapModifier modifier = new MapModifier() .putAll(commonProperties) .put("username", "Ben") .put("accountStatus", LOCKED); // 从 Supplier 中获取 Map,并修改数据 - HashMap hashMap1 = modifier.getAndModify(HashMap::new); + HashMap hashMap1 = modifier.getAndModify(HashMap::new); assertEquals(expected, hashMap1); // 可以灵活使用不同 Map 类型的不同构造器 - HashMap hashMap2 = modifier.getAndModify(() -> new HashMap<>(8)); + HashMap hashMap2 = modifier.getAndModify(() -> new HashMap<>(8)); assertEquals(expected, hashMap2); - // HashMap hashMap3 = modifier.getAndModify(() -> new HashMap<>(anotherMap)); - TreeMap treeMap = modifier.getAndModify(TreeMap::new); + // HashMap hashMap3 = modifier.getAndModify(() -> new HashMap<>(anotherMap)); + TreeMap treeMap = modifier.getAndModify(TreeMap::new); assertEquals(expected, treeMap); - ConcurrentHashMap concurrentHashMap = modifier.getAndModify(ConcurrentHashMap::new); + ConcurrentHashMap concurrentHashMap = modifier.getAndModify(ConcurrentHashMap::new); assertEquals(expected, concurrentHashMap); + assertNull(modifier.getAndModify(() -> (Map) null)); + // 修改已有的 Map - Map srcMap = new HashMap<>(); + Map srcMap = new HashMap<>(); srcMap.put("srcKey1", "srcValue1"); srcMap.put("srcKey2", "srcValue2"); modifier.modify(srcMap); - assertEquals(new HashMap() { + assertEquals(new HashMap() { { putAll(commonProperties); put("username", "Ben"); @@ -89,8 +97,10 @@ public class MapModifierTests { } }, srcMap); + assertDoesNotThrow(() -> modifier.modify((Map) null)); + // 创建一个有初始化数据的不可变的 {@code Map} - Map unmodifiableMap = modifier.getUnmodifiableMap(); + Map unmodifiableMap = modifier.getUnmodifiableMap(); assertEquals(expected, unmodifiableMap); assertThrows(UnsupportedOperationException.class, () -> unmodifiableMap.put("key", "value")); @@ -99,13 +109,13 @@ public class MapModifierTests { @Test void createAndInitData() { // 链式调用创建并初始化数据 - HashMap map = new MapModifier() + HashMap map = new MapModifier() .putAll(commonProperties) .put("username", "Ben") .put("accountStatus", LOCKED) .getAndModify(HashMap::new); - HashMap expected = new HashMap() { + HashMap expected = new HashMap() { { put("channel", "MOBILE"); put("appStartId", APP_START_ID); @@ -115,4 +125,192 @@ public class MapModifierTests { }; assertEquals(expected, map); } + + @Test + void put() { + Map map = new MapModifier() + .put("key1", "value0") + .put("key1", "value1") + .getAndModify(HashMap::new); + + assertEquals(new HashMap() { + { + put("key1", "value0"); + put("key1", "value1"); + } + }, map); + + new MapModifier() + .put("key1", "newValue1") + .put("key2", null) + .modify(map); + + assertEquals("newValue1", map.get("key1")); + assertTrue(map.containsKey("key2")); + assertNull(map.get("key2")); + } + + @Test + void putIfAbsent() { + Map map = new MapModifier() + .putIfAbsent("key1", null) + .putIfAbsent("key1", "value1") + .putIfAbsent("key1", "value2") + .getAndModify(HashMap::new); + + assertEquals(new HashMap() { + { + putIfAbsent("key1", null); + putIfAbsent("key1", "value1"); + putIfAbsent("key1", "value2"); + } + }, map); + + new MapModifier() + .putIfAbsent("key1", "newValue1") + .modify(map); + + assertTrue(map.containsKey("key1")); + assertEquals("value1", map.get("key1")); + } + + @Test + void putAll_map() { + Map entries = new HashMap() { + { + put("key1", "value1"); + put("key2", "value2"); + } + }; + Map map = new MapModifier() + .putAll((Map) null) + .putAll(Collections.emptyMap()) + .putAll(entries) + .getAndModify(HashMap::new); + assertEquals(entries, map); + new MapModifier() + .putAll(new HashMap() { + { + put("key2", "newValue2"); + put("key3", "value3"); + } + }) + .modify(map); + assertEquals(new HashMap() { + { + put("key1", "value1"); + put("key2", "value2"); + put("key2", "newValue2"); + put("key3", "value3"); + } + }, map); + } + + @Test + void putAll_entries() { + Map entries = new HashMap() { + { + put("key1", "value1"); + put("key2", "value2"); + } + }; + Map map = new MapModifier() + .putAll(new SimpleEntry<>("key1", "value1"), + new SimpleEntry<>("key2", "value2")) + .getAndModify(HashMap::new); + assertEquals(entries, map); + new MapModifier() + .putAll() + .putAll(new SimpleEntry<>("key2", "newValue2"), + new SimpleEntry<>("key3", "value3")) + .modify(map); + assertEquals(new HashMap() { + { + put("key1", "value1"); + put("key2", "value2"); + put("key2", "newValue2"); + put("key3", "value3"); + } + }, map); + } + + @Test + void computeIfAbsent_keyAndFunction() { + Map map = new MapModifier() + .computeIfAbsent("key1", k -> null) + .computeIfAbsent("key1", k -> "value1") + .computeIfAbsent("key1", k -> "value2") + .getAndModify(HashMap::new); + + assertEquals(new HashMap() { + { + computeIfAbsent("key1", k -> null); + computeIfAbsent("key1", k -> "value1"); + computeIfAbsent("key1", k -> "value2"); + } + }, map); + + new MapModifier() + .computeIfAbsent("key1", k -> "newValue1") + .modify(map); + + assertTrue(map.containsKey("key1")); + assertEquals("value1", map.get("key1")); + } + + @Test + void computeIfPresent_keyAndBiFunction() { + Map map = new HashMap() {{ + put("key1", "value1"); + }}; + new MapModifier() + .computeIfPresent("key1", (k, v) -> k + v) + .computeIfPresent("key2", (k, v) -> k + v) + .modify(map); + assertEquals(new HashMap() {{ + put("key1", "key1value1"); + }}, map); + } + + @Test + void remove() { + Map map = new HashMap() {{ + put("key1", "value1"); + put("key2", "value2"); + }}; + new MapModifier() + .remove("key2") + .modify(map); + assertEquals(new HashMap() {{ + put("key1", "value1"); + }}, map); + } + + @Test + void clear() { + Map map = new HashMap() {{ + put("key1", "value1"); + put("key2", "value2"); + }}; + new MapModifier() + .clear() + .modify(map); + assertTrue(map.isEmpty()); + } + + @Getter + static class SimpleEntry implements Map.Entry { + private final K key; + private final V value; + + public SimpleEntry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public V setValue(@Nullable V value) { + throw new UnsupportedOperationException("Unimplemented method 'setValue'"); + } + } } -- 2.49.1