From 6c001e868bf8150e6ac864677e8d39863e7c79b7 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Thu, 25 Aug 2022 12:13:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=94=AF=E6=8C=81=E4=B8=80?= =?UTF-8?q?=E9=94=AE=E5=A4=9A=E5=80=BC=E7=9A=84Map=E9=9B=86=E5=90=88?= =?UTF-8?q?=E6=89=A9=E5=B1=95=EF=BC=8C=E4=B8=BA=E5=85=B6=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/map/MapWrapper.java | 15 +- .../core/map/multi/AbsCollValueMap.java | 168 +++++++----- .../core/map/multi/CollectionValueMap.java | 100 +++---- .../hutool/core/map/multi/ListValueMap.java | 69 ++--- .../hutool/core/map/multi/MultiValueMap.java | 250 ++++++++++++++++++ .../cn/hutool/core/map/multi/SetValueMap.java | 70 ++--- .../core/net/multipart/MultipartFormData.java | 23 +- .../core/map/CollectionValueMapTest.java | 160 +++++++++++ .../cn/hutool/core/map/ListValueMapTest.java | 160 +++++++++++ .../cn/hutool/core/map/SetValueMapTest.java | 162 ++++++++++++ 10 files changed, 941 insertions(+), 236 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/map/CollectionValueMapTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/map/ListValueMapTest.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/map/SetValueMapTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java b/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java index 95e4bb197..e1102da9d 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/MapWrapper.java @@ -1,16 +1,13 @@ package cn.hutool.core.map; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjUtil; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; @@ -35,7 +32,9 @@ public class MapWrapper implements Map, Iterable>, S * 默认初始大小 */ protected static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 - + /** + * 原始集合 + */ private Map raw; /** @@ -52,9 +51,11 @@ public class MapWrapper implements Map, Iterable>, S /** * 构造 * - * @param raw 被包装的Map + * @param raw 被包装的Map,不允许为{@code null} + * @throws NullPointerException 当被包装的集合为{@code null}时抛出 */ public MapWrapper(final Map raw) { + Assert.notNull(raw, "raw must not null"); this.raw = raw; } diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java index 7ccc48292..9c0e960d1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/AbsCollValueMap.java @@ -1,22 +1,29 @@ package cn.hutool.core.map.multi; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Opt; import cn.hutool.core.map.MapWrapper; +import cn.hutool.core.util.ObjUtil; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; /** - * 值作为集合的Map实现,通过调用putValue可以在相同key时加入多个值,多个值用集合表示 + * {@link MultiValueMap}的基本实现 * * @param 键类型 * @param 值类型 - * @param 集合类型 * @author looly * @since 5.7.4 + * @see SetValueMap + * @see ListValueMap */ -public abstract class AbsCollValueMap> extends MapWrapper { +public abstract class AbsCollValueMap extends MapWrapper> implements MultiValueMap { private static final long serialVersionUID = 1L; /** @@ -27,94 +34,132 @@ public abstract class AbsCollValueMap> extends Map // ------------------------------------------------------------------------- Constructor start /** - * 构造 + * 使用{@code mapFactory}创建的集合构造一个多值映射Map集合 + * + * @param mapFactory 生成集合的工厂方法 */ - public AbsCollValueMap() { - this(DEFAULT_INITIAL_CAPACITY); + protected AbsCollValueMap(Supplier>> mapFactory) { + super(mapFactory); } /** - * 构造 + * 基于{@link HashMap}构造一个多值映射集合 * - * @param initialCapacity 初始大小 + * @param map 提供初始数据的集合 */ - public AbsCollValueMap(final int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR); + protected AbsCollValueMap(Map> map) { + super(new HashMap<>(map)); } /** - * 构造 - * - * @param m Map + * 基于{@link HashMap}构造一个多值映射集合 */ - public AbsCollValueMap(final Map m) { - this(DEFAULT_LOAD_FACTOR, m); + protected AbsCollValueMap() { + super(new HashMap<>(16)); } - /** - * 构造 - * - * @param loadFactor 加载因子 - * @param m Map - */ - public AbsCollValueMap(final float loadFactor, final Map m) { - this(m.size(), loadFactor); - this.putAll(m); - } - - /** - * 构造 - * - * @param initialCapacity 初始大小 - * @param loadFactor 加载因子 - */ - public AbsCollValueMap(final int initialCapacity, final float loadFactor) { - super(new HashMap<>(initialCapacity, loadFactor)); - } // ------------------------------------------------------------------------- Constructor end + /** - * 放入所有value + * 将集合中的全部元素对追加到指定键对应的值集合中,效果等同于: + *
{@code
+	 * coll.forEach(t -> map.putValue(key, t))
+	 * }
* - * @param m valueMap - * @since 5.7.4 + * @param key 键 + * @param coll 待添加的值集合 + * @return 是否成功添加 */ - public void putAllValues(final Map> m) { - if(null != m){ - m.forEach((key, valueColl) -> { - if(null != valueColl){ - valueColl.forEach((value) -> putValue(key, value)); - } - }); + @Override + public boolean putAllValues(K key, Collection coll) { + if (ObjUtil.isNull(coll)) { + return false; } + return super.computeIfAbsent(key, k -> createCollection()) + .addAll(coll); } /** - * 放入Value
- * 如果键对应值列表有值,加入,否则创建一个新列表后加入 + * 向指定键对应的值集合追加值,效果等同于: + *
{@code
+	 * map.computeIfAbsent(key, k -> new Collection()).add(value)
+	 * }
* * @param key 键 * @param value 值 + * @return 是否成功添加 */ - public void putValue(final K key, final V value) { - C collection = this.get(key); - if (null == collection) { - collection = createCollection(); - this.put(key, collection); - } - collection.add(value); + @Override + public boolean putValue(K key, V value) { + return super.computeIfAbsent(key, k -> createCollection()) + .add(value); } /** - * 获取值 + * 将值从指定键下的值集合中删除 * * @param key 键 - * @param index 第几个值的索引,越界返回null - * @return 值或null + * @param value 值 + * @return 是否成功删除 */ - public V get(final K key, final int index) { - final Collection collection = get(key); - return CollUtil.get(collection, index); + @Override + public boolean removeValue(K key, V value) { + return Opt.ofNullable(super.get(key)) + .map(t -> t.remove(value)) + .orElse(false); + } + + /** + * 将一批值从指定键下的值集合中删除 + * + * @param key 键 + * @param values 值 + * @return 是否成功删除 + */ + @Override + public boolean removeAllValues(K key, Collection values) { + if (CollUtil.isEmpty(values)) { + return false; + } + Collection coll = get(key); + return ObjUtil.isNotNull(coll) && coll.removeAll(values); + } + + /** + * 根据条件过滤所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param filter 判断方法 + * @return 当前实例 + */ + @Override + public MultiValueMap filterAllValues(BiPredicate filter) { + entrySet().forEach(e -> { + K k = e.getKey(); + Collection coll = e.getValue().stream() + .filter(v -> filter.test(k, v)) + .collect(Collectors.toCollection(this::createCollection)); + e.setValue(coll); + }); + return this; + } + + /** + * 根据条件替换所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param operate 替换方法 + * @return 当前实例 + */ + @Override + public MultiValueMap replaceAllValues(BiFunction operate) { + entrySet().forEach(e -> { + K k = e.getKey(); + Collection coll = e.getValue().stream() + .map(v -> operate.apply(k, v)) + .collect(Collectors.toCollection(this::createCollection)); + e.setValue(coll); + }); + return this; } /** @@ -123,5 +168,6 @@ public abstract class AbsCollValueMap> extends Map * * @return {@link Collection} */ - protected abstract C createCollection(); + protected abstract Collection createCollection(); + } diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/CollectionValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/CollectionValueMap.java index 3f9dd8bcf..e798c7602 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/multi/CollectionValueMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/CollectionValueMap.java @@ -6,97 +6,67 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; /** - * 值作为集合的Map实现,通过调用putValue可以在相同key时加入多个值,多个值用集合表示
- * 此类可以通过传入函数自定义集合类型的创建规则 + *

{@link MultiValueMap}的通用实现,可视为值为{@link Collection}集合的{@link Map}集合。
+ * 构建时指定一个工厂方法用于生成原始的{@link Map}集合,然后再指定一个工厂方法用于生成自定义类型的值集合。
+ * 当调用{@link MultiValueMap}中格式为“putXXX”的方法时,将会为key创建值集合,并将key相同的值追加到集合中 * * @param 键类型 * @param 值类型 * @author looly * @since 4.3.3 */ -public class CollectionValueMap extends AbsCollValueMap> { +public class CollectionValueMap extends AbsCollValueMap { + private static final long serialVersionUID = 9012989578038102983L; - private final Func0> collectionCreateFunc; + private final Func0> collFactory; // ------------------------------------------------------------------------- Constructor start /** - * 构造 + * 创建一个多值映射集合,基于{@code mapFactory}与{@code collFactory}实现 + * + * @param mapFactory 生成集合的工厂方法 + * @param collFactory 生成值集合的工厂方法 + */ + public CollectionValueMap(Supplier>> mapFactory, Func0> collFactory) { + super(mapFactory); + this.collFactory = collFactory; + } + + /** + * 创建一个多值映射集合,默认基于{@link HashMap}与{@code collFactory}生成的集合实现 + * + * @param collFactory 生成值集合的工厂方法 + */ + public CollectionValueMap(Func0> collFactory) { + this.collFactory = collFactory; + } + + /** + * 创建一个多值映射集合,默认基于{@link HashMap}与{@link ArrayList}实现 */ public CollectionValueMap() { - this(DEFAULT_INITIAL_CAPACITY); + this.collFactory = ArrayList::new; } /** - * 构造 + * 创建一个多值映射集合,默认基于{@link HashMap}与{@link ArrayList}实现 * - * @param initialCapacity 初始大小 + * @param map 提供数据的原始集合 */ - public CollectionValueMap(final int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR); + public CollectionValueMap(Map> map) { + super(map); + this.collFactory = ArrayList::new; } - /** - * 构造 - * - * @param m Map - */ - public CollectionValueMap(final Map> m) { - this(DEFAULT_LOAD_FACTOR, m); - } - - /** - * 构造 - * - * @param loadFactor 加载因子 - * @param m Map - */ - public CollectionValueMap(final float loadFactor, final Map> m) { - this(loadFactor, m, ArrayList::new); - } - - /** - * 构造 - * - * @param initialCapacity 初始大小 - * @param loadFactor 加载因子 - */ - public CollectionValueMap(final int initialCapacity, final float loadFactor) { - this(initialCapacity, loadFactor, ArrayList::new); - } - - /** - * 构造 - * - * @param loadFactor 加载因子 - * @param m Map - * @param collectionCreateFunc Map中值的集合创建函数 - * @since 5.7.4 - */ - public CollectionValueMap(final float loadFactor, final Map> m, final Func0> collectionCreateFunc) { - this(m.size(), loadFactor, collectionCreateFunc); - this.putAll(m); - } - - /** - * 构造 - * - * @param initialCapacity 初始大小 - * @param loadFactor 加载因子 - * @param collectionCreateFunc Map中值的集合创建函数 - * @since 5.7.4 - */ - public CollectionValueMap(final int initialCapacity, final float loadFactor, final Func0> collectionCreateFunc) { - super(new HashMap<>(initialCapacity, loadFactor)); - this.collectionCreateFunc = collectionCreateFunc; - } // ------------------------------------------------------------------------- Constructor end @Override protected Collection createCollection() { - return collectionCreateFunc.callWithRuntimeException(); + return collFactory.callWithRuntimeException(); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/ListValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/ListValueMap.java index ab3b29c33..90f79cc01 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/multi/ListValueMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/ListValueMap.java @@ -1,10 +1,7 @@ package cn.hutool.core.map.multi; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.function.Supplier; /** * 值作为集合List的Map实现,通过调用putValue可以在相同key时加入多个值,多个值用集合表示 @@ -15,55 +12,35 @@ import java.util.Map; * @param 值类型 * @since 4.3.3 */ -public class ListValueMap extends AbsCollValueMap> { +public class ListValueMap extends AbsCollValueMap { private static final long serialVersionUID = 6044017508487827899L; // ------------------------------------------------------------------------- Constructor start + /** - * 构造 + * 基于{@code mapFactory}创建一个值为{@link List}的多值映射集合 + * + * @param mapFactory 创建集合的工厂反方 + */ + public ListValueMap(Supplier>> mapFactory) { + super(mapFactory); + } + + /** + * 基于{@link HashMap}创建一个值为{@link List}的多值映射集合 + * + * @param map 提供数据的原始集合 + */ + public ListValueMap(Map> map) { + super(map); + } + + /** + * 基于{@link HashMap}创建一个值为{@link List}的多值映射集合 */ public ListValueMap() { - this(DEFAULT_INITIAL_CAPACITY); } - /** - * 构造 - * - * @param initialCapacity 初始大小 - */ - public ListValueMap(final int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR); - } - - /** - * 构造 - * - * @param m Map - */ - public ListValueMap(final Map> m) { - this(DEFAULT_LOAD_FACTOR, m); - } - - /** - * 构造 - * - * @param loadFactor 加载因子 - * @param m Map - */ - public ListValueMap(final float loadFactor, final Map> m) { - this(m.size(), loadFactor); - this.putAllValues(m); - } - - /** - * 构造 - * - * @param initialCapacity 初始大小 - * @param loadFactor 加载因子 - */ - public ListValueMap(final int initialCapacity, final float loadFactor) { - super(new HashMap<>(initialCapacity, loadFactor)); - } // ------------------------------------------------------------------------- Constructor end @Override diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java new file mode 100644 index 000000000..684d83a54 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java @@ -0,0 +1,250 @@ +package cn.hutool.core.map.multi; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; + +import java.util.*; +import java.util.function.*; +import java.util.stream.Collectors; + +/** + *

一个键对应多个值的集合{@link Map}实现,提供针对键对应的值集合中的元素而非值集合本身的一些快捷操作, + * 本身可作为一个值为{@link Collection}类型的{@link Map}使用。
+ * + *

值集合类型

+ *

值集合的类型由接口的实现类自行维护,当通过{@link MultiValueMap}定义的方法进行增删改操作时, + * 实现类应保证通过通过实例方法获得的集合类型都一致。但是若用户直接通过{@link Map}定义的方法进行增删改操作时, + * 实例无法保证通过实例方法获得的集合类型都一致。
+ * 因此,若无必要则更推荐通过{@link MultiValueMap}定义的方法进行操作。 + * + *

对值集合的修改

+ *

当通过实例方法获得值集合时,若该集合允许修改,则对值集合的修改将会影响到其所属的{@link MultiValueMap}实例,反之亦然。 + * 因此当同时遍历当前实例或者值集合时,若存在写操作,则需要注意可能引发的{@link ConcurrentModificationException}。 + * + * @author huangchengxing + * @since 6.0.0 + * @see AbsCollValueMap + * @see CollectionValueMap + * @see ListValueMap + * @see SetValueMap + */ +public interface MultiValueMap extends Map> { + + // =================== override =================== + + /** + * 更新键对应的值集合
+ * 注意:该操作将移除键对应的旧值集合,若仅需向值集合追加应值,则应使用{@link #putAllValues(Object, Collection)} + * + * @param key 键 + * @param value 键对应的新值集合 + * @return 旧值集合 + */ + @Override + Collection put(K key, Collection value); + + /** + * 更新全部键的值集合
+ * 注意:该操作将移除键对应的旧值集合,若仅需向值集合追加应值,则应使用{@link #putAllValues(Object, Collection)} + * + * @param map 需要更新的键值对集合 + */ + @Override + void putAll(Map> map); + + // =================== write operate =================== + + /** + * 将集合中的全部键值对追加到当前实例中,效果等同于: + *

{@code
+	 * for (Entry> entry : m.entrySet()) {
+	 * 	K key = entry.getKey();
+	 * 	Collection coll = entry.getValues();
+	 * 	for (V val : coll) {
+	 * 		map.putValue(key, val)
+	 * 	}
+	 * }
+	 * }
+ * + * @param m 待添加的集合 + */ + default void putAllValues(final Map> m) { + if (CollUtil.isNotEmpty(m)) { + m.forEach(this::putAllValues); + } + } + + /** + * 将集合中的全部元素对追加到指定键对应的值集合中,效果等同于: + *
{@code
+	 * 	for (V val : coll) {
+	 * 		map.putValue(key, val)
+	 * 	}
+	 * }
+ * + * @param key 键 + * @param coll 待添加的值集合 + * @return 是否成功添加 + */ + boolean putAllValues(K key, final Collection coll); + + /** + * 将数组中的全部元素追加到指定的值集合中,效果等同于: + *
{@code
+	 * 	for (V val : values) {
+	 * 		map.putValue(key, val)
+	 * 	}
+	 * }
+ * + * @param key 键 + * @param values 待添加的值 + * @return boolean + */ + default boolean putValues(final K key, final V... values) { + return ArrayUtil.isNotEmpty(values) && putAllValues(key, Arrays.asList(values)); + } + + /** + * 向指定键对应的值集合追加值,效果等同于: + *
{@code
+	 * Collection coll = map.get(key);
+	 * if(null == coll) {
+	 * 	coll.add(value);
+	 * 	map.put(coll);
+	 * } else {
+	 * 	coll.add(value);
+	 * }
+	 * }
+ * + * @param key 键 + * @param value 值 + * @return 是否成功添加 + */ + boolean putValue(final K key, final V value); + + /** + * 将值从指定键下的值集合中删除 + * + * @param key 键 + * @param value 值 + * @return 是否成功删除 + */ + boolean removeValue(final K key, final V value); + + /** + * 将一批值从指定键下的值集合中删除 + * + * @param key 键 + * @param values 值数组 + * @return 是否成功删除 + */ + default boolean removeValues(final K key, final V... values) { + return ArrayUtil.isNotEmpty(values) && removeAllValues(key, Arrays.asList(values)); + } + + /** + * 将一批值从指定键下的值集合中删除 + * + * @param key 键 + * @param values 值集合 + * @return 是否成功删除 + */ + boolean removeAllValues(final K key, final Collection values); + + /** + * 根据条件过滤所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param filter 判断方法 + * @return 当前实例 + */ + default MultiValueMap filterAllValues(Predicate filter) { + return filterAllValues((k, v) -> filter.test(v)); + } + + /** + * 根据条件过滤所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param filter 判断方法 + * @return 当前实例 + */ + MultiValueMap filterAllValues(BiPredicate filter); + + /** + * 根据条件替换所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param operate 替换方法 + * @return 当前实例 + */ + default MultiValueMap replaceAllValues(UnaryOperator operate) { + return replaceAllValues((k, v) -> operate.apply(v)); + } + + /** + * 根据条件替换所有值集合中的值,并以新值生成新的值集合,新集合中的值集合类型与当前实例的默认值集合类型保持一致 + * + * @param operate 替换方法 + * @return 当前实例 + */ + MultiValueMap replaceAllValues(BiFunction operate); + + // =================== read operate =================== + + /** + * 获取键对应的值,若值不存在,则返回{@link Collections#emptyList()}。效果等同于: + *
{@code
+	 * map.getOrDefault(key, Collections.emptyList())
+	 * }
+ * + * @param key 键 + * @return 值集合 + */ + default Collection getValues(final K key) { + return getOrDefault(key, Collections.emptyList()); + } + + /** + * 获取键对应值的数量,若键对应的值不存在,则返回{@code 0} + * + * @param key 键 + * @return 值的数量 + */ + default int size(final K key) { + return getValues(key).size(); + } + + /** + * 遍历所有键值对,效果等同于: + *
{@code
+	 * for (Entry> entry : entrySet()) {
+	 * 	K key = entry.getKey();
+	 * 	Collection coll = entry.getValues();
+	 * 	for (V val : coll) {
+	 * 		consumer.accept(key, val);
+	 * 	}
+	 * }
+	 * }
+ * + * @param consumer 操作 + */ + default void allForEach(BiConsumer consumer) { + forEach((k, coll) -> coll.forEach(v -> consumer.accept(k, v))); + } + + /** + * 获取所有的值,效果等同于: + *
{@code
+	 * List results = new ArrayList<>();
+	 * for (Collection coll : values()) {
+	 * 	results.addAll(coll);
+	 * }
+	 * }
+ * + * @return 值 + */ + default Collection allValues() { + return values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/SetValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/SetValueMap.java index 05a4fdcac..7b76611c7 100644 --- a/hutool-core/src/main/java/cn/hutool/core/map/multi/SetValueMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/SetValueMap.java @@ -1,10 +1,7 @@ package cn.hutool.core.map.multi; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.function.Supplier; /** * 值作为集合Set(LinkedHashSet)的Map实现,通过调用putValue可以在相同key时加入多个值,多个值用集合表示 @@ -15,59 +12,40 @@ import java.util.Set; * @param 值类型 * @since 4.3.3 */ -public class SetValueMap extends AbsCollValueMap> { +public class SetValueMap extends AbsCollValueMap { private static final long serialVersionUID = 6044017508487827899L; // ------------------------------------------------------------------------- Constructor start + /** - * 构造 + * 基于{@code mapFactory}创建一个值为{@link Set}的多值映射集合 + * + * @param mapFactory 创建集合的工厂反方 + */ + public SetValueMap(Supplier>> mapFactory) { + super(mapFactory); + } + + /** + * 基于{@link HashMap}创建一个值为{@link Set}的多值映射集合 + * + * @param map 提供数据的原始集合 + */ + public SetValueMap(Map> map) { + super(map); + } + + /** + * 基于{@link HashMap}创建一个值为{@link Set}的多值映射集合 */ public SetValueMap() { - this(DEFAULT_INITIAL_CAPACITY); } - /** - * 构造 - * - * @param initialCapacity 初始大小 - */ - public SetValueMap(final int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR); - } - - /** - * 构造 - * - * @param m Map - */ - public SetValueMap(final Map> m) { - this(DEFAULT_LOAD_FACTOR, m); - } - - /** - * 构造 - * - * @param loadFactor 加载因子 - * @param m Map - */ - public SetValueMap(final float loadFactor, final Map> m) { - this(m.size(), loadFactor); - this.putAllValues(m); - } - - /** - * 构造 - * - * @param initialCapacity 初始大小 - * @param loadFactor 加载因子 - */ - public SetValueMap(final int initialCapacity, final float loadFactor) { - super(new HashMap<>(initialCapacity, loadFactor)); - } // ------------------------------------------------------------------------- Constructor end @Override protected Set createCollection() { return new LinkedHashSet<>(DEFAULT_COLLECTION_INITIAL_CAPACITY); } + } diff --git a/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java b/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java index 34ff7521d..dfb173352 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/multipart/MultipartFormData.java @@ -3,11 +3,12 @@ package cn.hutool.core.net.multipart; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.map.multi.ListValueMap; +import cn.hutool.core.map.multi.MultiValueMap; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; -import java.util.List; +import java.util.Collection; import java.util.Map; import java.util.Set; @@ -20,9 +21,9 @@ import java.util.Set; public class MultipartFormData { /** 请求参数 */ - private final ListValueMap requestParameters = new ListValueMap<>(); + private final MultiValueMap requestParameters = new ListValueMap<>(); /** 请求文件 */ - private final ListValueMap requestFiles = new ListValueMap<>(); + private final MultiValueMap requestFiles = new ListValueMap<>(); /** 上传选项 */ private final UploadSetting setting; @@ -101,9 +102,9 @@ public class MultipartFormData { * @return null未找到,否则返回值 */ public String getParam(final String paramName) { - final List values = getListParam(paramName); + final Collection values = getListParam(paramName); if (CollUtil.isNotEmpty(values)) { - return values.get(0); + return CollUtil.get(values, 0); } return null; } @@ -122,7 +123,7 @@ public class MultipartFormData { * @return 数组表单值 */ public String[] getArrayParam(final String paramName) { - final List listParam = getListParam(paramName); + final Collection listParam = getListParam(paramName); if(null != listParam){ return listParam.toArray(new String[0]); } @@ -136,7 +137,7 @@ public class MultipartFormData { * @return 数组表单值 * @since 5.3.0 */ - public List getListParam(final String paramName) { + public Collection getListParam(final String paramName) { return requestParameters.get(paramName); } @@ -154,7 +155,7 @@ public class MultipartFormData { * * @return 所有属性的集合 */ - public ListValueMap getParamListMap() { + public MultiValueMap getParamListMap() { return this.requestParameters; } @@ -181,7 +182,7 @@ public class MultipartFormData { * @return 上传的文件列表 */ public UploadFile[] getFiles(final String paramName) { - final List fileList = getFileList(paramName); + final Collection fileList = getFileList(paramName); if(null != fileList){ return fileList.toArray(new UploadFile[0]); } @@ -196,7 +197,7 @@ public class MultipartFormData { * @return 上传的文件列表 * @since 5.3.0 */ - public List getFileList(final String paramName) { + public Collection getFileList(final String paramName) { return requestFiles.get(paramName); } @@ -223,7 +224,7 @@ public class MultipartFormData { * * @return 文件映射 */ - public ListValueMap getFileListValueMap() { + public MultiValueMap getFileListValueMap() { return this.requestFiles; } diff --git a/hutool-core/src/test/java/cn/hutool/core/map/CollectionValueMapTest.java b/hutool-core/src/test/java/cn/hutool/core/map/CollectionValueMapTest.java new file mode 100644 index 000000000..e624bcc6b --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/map/CollectionValueMapTest.java @@ -0,0 +1,160 @@ +package cn.hutool.core.map; + +import cn.hutool.core.map.multi.CollectionValueMap; +import cn.hutool.core.map.multi.MultiValueMap; +import cn.hutool.core.text.StrUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; + +public class CollectionValueMapTest { + + @Test + public void putTest() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertNull(map.put(1, Arrays.asList("a", "b"))); + Collection collection = map.put(1, Arrays.asList("c", "d")); + Assert.assertEquals(Arrays.asList("a", "b"), collection); + } + + @Test + public void putAllTest() { + MultiValueMap map = new CollectionValueMap<>(); + Map> source = new HashMap<>(); + source.put(1, Arrays.asList("a", "b", "c")); + map.putAll(source); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void putValueTest() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertTrue(map.putValue(1, "a")); + Assert.assertTrue(map.putValue(1, "b")); + Assert.assertTrue(map.putValue(1, "c")); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void putAllValueTest() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertTrue(map.putAllValues(1, Arrays.asList("a", "b", "c"))); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + + Map> source = new HashMap<>(); + Assert.assertTrue(map.putValue(1, "e")); + Assert.assertTrue(map.putValue(1, "f")); + map.putAllValues(source); + Assert.assertEquals(Arrays.asList("a", "b", "c", "e", "f"), map.get(1)); + } + + @Test + public void putValuesTest() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void testFilterAllValues() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.filterAllValues((k, v) -> StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.singletonList("a"), map.getValues(1)); + Assert.assertEquals(Collections.singletonList("a"), map.getValues(2)); + + Assert.assertEquals(map, map.filterAllValues(v -> !StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.emptyList(), map.getValues(1)); + Assert.assertEquals(Collections.emptyList(), map.getValues(2)); + } + + @Test + public void testReplaceAllValues() { + MultiValueMap map = new CollectionValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.replaceAllValues((k, v) -> v + "2")); + Assert.assertEquals(Arrays.asList("a2", "b2", "c2"), map.getValues(1)); + Assert.assertEquals(Arrays.asList("a2", "b2", "c2"), map.getValues(2)); + + Assert.assertEquals(map, map.replaceAllValues(v -> v + "3")); + Assert.assertEquals(Arrays.asList("a23", "b23", "c23"), map.getValues(1)); + Assert.assertEquals(Arrays.asList("a23", "b23", "c23"), map.getValues(2)); + } + + @Test + public void removeValueTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValue(1, "d")); + Assert.assertTrue(map.removeValue(1, "c")); + Assert.assertEquals(Arrays.asList("a", "b"), map.get(1)); + } + + @Test + public void removeAllValuesTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeAllValues(1, Arrays.asList("e", "f"))); + Assert.assertTrue(map.removeAllValues(1, Arrays.asList("b", "c"))); + Assert.assertEquals(Collections.singletonList("a"), map.get(1)); + } + + @Test + public void removeValuesTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValues(1, "e", "f")); + Assert.assertTrue(map.removeValues(1, "b", "c")); + Assert.assertEquals(Collections.singletonList("a"), map.get(1)); + } + + @Test + public void getValuesTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(Collections.emptyList(), map.getValues(2)); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.getValues(1)); + } + + @Test + public void sizeTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(0, map.size(2)); + Assert.assertEquals(3, map.size(1)); + } + + @Test + public void allForEachTest() { + MultiValueMap map = new CollectionValueMap<>(); + map.putValues(1, "a", "b", "c"); + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + map.allForEach((k, v) -> { + keys.add(k); + values.add(v); + }); + Assert.assertEquals(Arrays.asList(1, 1, 1), keys); + Assert.assertEquals(Arrays.asList("a", "b", "c"), values); + } + + @Test + public void allValuesTest() { + MultiValueMap map = new CollectionValueMap<>(new LinkedHashMap<>()); + map.putAllValues(1, Arrays.asList("a", "b", "c")); + map.putAllValues(2, Arrays.asList("d", "e")); + Assert.assertEquals( + Arrays.asList("a", "b", "c", "d", "e"), + map.allValues() + ); + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/map/ListValueMapTest.java b/hutool-core/src/test/java/cn/hutool/core/map/ListValueMapTest.java new file mode 100644 index 000000000..b607127c5 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/map/ListValueMapTest.java @@ -0,0 +1,160 @@ +package cn.hutool.core.map; + +import cn.hutool.core.map.multi.ListValueMap; +import cn.hutool.core.map.multi.MultiValueMap; +import cn.hutool.core.text.StrUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; + +public class ListValueMapTest { + + @Test + public void putTest() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertNull(map.put(1, Arrays.asList("a", "b"))); + Collection collection = map.put(1, Arrays.asList("c", "d")); + Assert.assertEquals(Arrays.asList("a", "b"), collection); + } + + @Test + public void putAllTest() { + MultiValueMap map = new ListValueMap<>(); + Map> source = new HashMap<>(); + source.put(1, Arrays.asList("a", "b", "c")); + map.putAll(source); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void putValueTest() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertTrue(map.putValue(1, "a")); + Assert.assertTrue(map.putValue(1, "b")); + Assert.assertTrue(map.putValue(1, "c")); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void putAllValueTest() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertTrue(map.putAllValues(1, Arrays.asList("a", "b", "c"))); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + + Map> source = new HashMap<>(); + Assert.assertTrue(map.putValue(1, "e")); + Assert.assertTrue(map.putValue(1, "f")); + map.putAllValues(source); + Assert.assertEquals(Arrays.asList("a", "b", "c", "e", "f"), map.get(1)); + } + + @Test + public void putValuesTest() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void removeValueTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValue(1, "d")); + Assert.assertTrue(map.removeValue(1, "c")); + Assert.assertEquals(Arrays.asList("a", "b"), map.get(1)); + } + + @Test + public void removeAllValuesTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeAllValues(1, Arrays.asList("e", "f"))); + Assert.assertTrue(map.removeAllValues(1, Arrays.asList("b", "c"))); + Assert.assertEquals(Collections.singletonList("a"), map.get(1)); + } + + @Test + public void removeValuesTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValues(1, "e", "f")); + Assert.assertTrue(map.removeValues(1, "b", "c")); + Assert.assertEquals(Collections.singletonList("a"), map.get(1)); + } + + @Test + public void testFilterAllValues() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.filterAllValues((k, v) -> StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.singletonList("a"), map.getValues(1)); + Assert.assertEquals(Collections.singletonList("a"), map.getValues(2)); + + Assert.assertEquals(map, map.filterAllValues(v -> !StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.emptyList(), map.getValues(1)); + Assert.assertEquals(Collections.emptyList(), map.getValues(2)); + } + + @Test + public void testReplaceAllValues() { + MultiValueMap map = new ListValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.replaceAllValues((k, v) -> v + "2")); + Assert.assertEquals(Arrays.asList("a2", "b2", "c2"), map.getValues(1)); + Assert.assertEquals(Arrays.asList("a2", "b2", "c2"), map.getValues(2)); + + Assert.assertEquals(map, map.replaceAllValues(v -> v + "3")); + Assert.assertEquals(Arrays.asList("a23", "b23", "c23"), map.getValues(1)); + Assert.assertEquals(Arrays.asList("a23", "b23", "c23"), map.getValues(2)); + } + + @Test + public void getValuesTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(Collections.emptyList(), map.getValues(2)); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.getValues(1)); + } + + @Test + public void sizeTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(0, map.size(2)); + Assert.assertEquals(3, map.size(1)); + } + + @Test + public void allForEachTest() { + MultiValueMap map = new ListValueMap<>(); + map.putValues(1, "a", "b", "c"); + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + map.allForEach((k, v) -> { + keys.add(k); + values.add(v); + }); + Assert.assertEquals(Arrays.asList(1, 1, 1), keys); + Assert.assertEquals(Arrays.asList("a", "b", "c"), values); + } + + @Test + public void allValuesTest() { + MultiValueMap map = new ListValueMap<>(); + map.putAllValues(1, Arrays.asList("a", "b", "c")); + map.putAllValues(2, Arrays.asList("d", "e")); + Assert.assertEquals( + Arrays.asList("a", "b", "c", "d", "e"), + map.allValues() + ); + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/map/SetValueMapTest.java b/hutool-core/src/test/java/cn/hutool/core/map/SetValueMapTest.java new file mode 100644 index 000000000..e41e5f872 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/map/SetValueMapTest.java @@ -0,0 +1,162 @@ +package cn.hutool.core.map; + +import cn.hutool.core.map.multi.MultiValueMap; +import cn.hutool.core.map.multi.SetValueMap; +import cn.hutool.core.text.StrUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; + +public class SetValueMapTest { + + @Test + public void putTest() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertNull(map.put(1, Arrays.asList("a", "b"))); + Collection collection = map.put(1, Arrays.asList("c", "d")); + Assert.assertEquals(Arrays.asList("a", "b"), collection); + } + + @Test + public void putAllTest() { + MultiValueMap map = new SetValueMap<>(); + Map> source = new HashMap<>(); + source.put(1, Arrays.asList("a", "b", "c")); + map.putAll(source); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(Arrays.asList("a", "b", "c"), map.get(1)); + } + + @Test + public void putValueTest() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertTrue(map.putValue(1, "a")); + Assert.assertTrue(map.putValue(1, "b")); + Assert.assertTrue(map.putValue(1, "c")); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b", "c")), map.get(1)); + } + + @Test + public void putAllValueTest() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertTrue(map.putAllValues(1, Arrays.asList("a", "b", "c"))); + Assert.assertEquals(1, map.size()); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b", "c")), map.get(1)); + + Map> source = new HashMap<>(); + Assert.assertTrue(map.putValue(1, "e")); + Assert.assertFalse(map.putValue(1, "e")); + Assert.assertTrue(map.putValue(1, "f")); + Assert.assertFalse(map.putValue(1, "f")); + map.putAllValues(source); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b", "c", "e", "f")), map.get(1)); + } + + @Test + public void putValuesTest() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b", "c")), map.get(1)); + } + + @Test + public void removeValueTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValue(1, "d")); + Assert.assertTrue(map.removeValue(1, "c")); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b")), map.get(1)); + } + + @Test + public void removeAllValuesTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeAllValues(1, Arrays.asList("e", "f"))); + Assert.assertTrue(map.removeAllValues(1, Arrays.asList("b", "c"))); + Assert.assertEquals(Collections.singleton("a"), map.get(1)); + } + + @Test + public void removeValuesTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertFalse(map.removeValues(1, "e", "f")); + Assert.assertTrue(map.removeValues(1, "b", "c")); + Assert.assertEquals(Collections.singleton("a"), map.get(1)); + } + + @Test + public void testFilterAllValues() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.filterAllValues((k, v) -> StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.singleton("a"), map.getValues(1)); + Assert.assertEquals(Collections.singleton("a"), map.getValues(2)); + + Assert.assertEquals(map, map.filterAllValues(v -> !StrUtil.equals(v, "a"))); + Assert.assertEquals(Collections.emptySet(), map.getValues(1)); + Assert.assertEquals(Collections.emptySet(), map.getValues(2)); + } + + @Test + public void testReplaceAllValues() { + MultiValueMap map = new SetValueMap<>(); + Assert.assertTrue(map.putValues(1, "a", "b", "c")); + Assert.assertTrue(map.putValues(2, "a", "b", "c")); + + Assert.assertEquals(map, map.replaceAllValues((k, v) -> v + "2")); + Assert.assertEquals(new HashSet<>(Arrays.asList("a2", "b2", "c2")), map.getValues(1)); + Assert.assertEquals(new HashSet<>(Arrays.asList("a2", "b2", "c2")), map.getValues(2)); + + Assert.assertEquals(map, map.replaceAllValues(v -> v + "3")); + Assert.assertEquals(new HashSet<>(Arrays.asList("a23", "b23", "c23")), map.getValues(1)); + Assert.assertEquals(new HashSet<>(Arrays.asList("a23", "b23", "c23")), map.getValues(2)); + } + + @Test + public void getValuesTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(Collections.emptyList(), map.getValues(2)); + Assert.assertEquals(new HashSet<>(Arrays.asList("a", "b", "c")), map.getValues(1)); + } + + @Test + public void sizeTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + Assert.assertEquals(0, map.size(2)); + Assert.assertEquals(3, map.size(1)); + } + + @Test + public void allForEachTest() { + MultiValueMap map = new SetValueMap<>(); + map.putValues(1, "a", "b", "c"); + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + map.allForEach((k, v) -> { + keys.add(k); + values.add(v); + }); + Assert.assertEquals(Arrays.asList(1, 1, 1), keys); + Assert.assertEquals(Arrays.asList("a", "b", "c"), values); + } + + @Test + public void allValuesTest() { + MultiValueMap map = new SetValueMap<>(); + map.putAllValues(1, Arrays.asList("a", "b", "c")); + map.putAllValues(2, Arrays.asList("d", "e")); + Assert.assertEquals( + Arrays.asList("a", "b", "c", "d", "e"), + map.allValues() + ); + } + +}