From 17be56a99c71bc9346d60789618da5e69fe22f34 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 2 Apr 2020 17:50:07 +0800 Subject: [PATCH] fix code --- CHANGELOG.md | 3 + .../cn/hutool/core/collection/CollUtil.java | 93 +++++- .../cn/hutool/core/collection/IterUtil.java | 312 ++++++++++-------- .../main/java/cn/hutool/core/net/NetUtil.java | 29 +- .../hutool/core/collection/CollUtilTest.java | 47 +++ .../hutool/core/collection/IterUtilTest.java | 17 +- .../java/cn/hutool/core/net/NetUtilTest.java | 16 + .../cn/hutool/extra/servlet/ServletUtil.java | 54 +-- .../cn/hutool/http/server/HttpRequest.java | 132 +++++++- 9 files changed, 498 insertions(+), 205 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6c1dfaa5..c9532ee0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ * 【script 】 增加createXXXScript,区别单例 * 【core 】 修改FileUtil.writeFileToStream等方法返回值为long * 【core 】 CollUtil.split增加空集合判定(issue#814@Github) +* 【core 】 NetUtil增加parseCookies方法 +* 【core 】 CollUtil增加toMap方法 +* 【core 】 CollUtil和IterUtil废弃一些方法 ### Bug修复 * 【extra 】 修复SpringUtil使用devtools重启报错问题 diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java index 5a92c8fa6..adbae5435 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java @@ -9,6 +9,7 @@ import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Editor; import cn.hutool.core.lang.Filter; import cn.hutool.core.lang.Matcher; +import cn.hutool.core.lang.func.Func1; import cn.hutool.core.lang.hash.Hash32; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ArrayUtil; @@ -245,7 +246,7 @@ public class CollUtil { * * @param coll1 集合1 * @param coll2 集合2 - * @param 元素类型 + * @param 元素类型 * @return 单差集 */ public static Collection subtract(Collection coll1, Collection coll2) { @@ -327,10 +328,10 @@ public class CollUtil { * @param 集合元素类型 * @param collection 集合 * @return {@link Map} - * @see IterUtil#countMap(Iterable) + * @see IterUtil#countMap(Iterator) */ public static Map countMap(Iterable collection) { - return IterUtil.countMap(collection); + return IterUtil.countMap(null == collection ? null : collection.iterator()); } /** @@ -341,10 +342,13 @@ public class CollUtil { * @param iterable {@link Iterable} * @param conjunction 分隔符 * @return 连接后的字符串 - * @see IterUtil#join(Iterable, CharSequence) + * @see IterUtil#join(Iterator, CharSequence) */ public static String join(Iterable iterable, CharSequence conjunction) { - return IterUtil.join(iterable, conjunction); + if (null == iterable) { + return null; + } + return IterUtil.join(iterable.iterator(), conjunction); } /** @@ -355,8 +359,9 @@ public class CollUtil { * @param iterator 集合 * @param conjunction 分隔符 * @return 连接后的字符串 - * @see IterUtil#join(Iterator, CharSequence) + * @deprecated 请使用IterUtil#join(Iterator, CharSequence) */ + @Deprecated public static String join(Iterator iterator, CharSequence conjunction) { return IterUtil.join(iterator, conjunction); } @@ -643,8 +648,8 @@ public class CollUtil { * 新建一个List
* 提供的参数为null时返回空{@link ArrayList} * - * @param 集合元素类型 - * @param isLinked 是否新建LinkedList + * @param 集合元素类型 + * @param isLinked 是否新建LinkedList * @param enumeration {@link Enumeration} * @return ArrayList对象 * @since 3.0.8 @@ -707,7 +712,7 @@ public class CollUtil { * 新建一个ArrayList
* 提供的参数为null时返回空{@link ArrayList} * - * @param 集合元素类型 + * @param 集合元素类型 * @param iterator {@link Iterator} * @return ArrayList对象 * @since 3.0.8 @@ -720,7 +725,7 @@ public class CollUtil { * 新建一个ArrayList
* 提供的参数为null时返回空{@link ArrayList} * - * @param 集合元素类型 + * @param 集合元素类型 * @param enumeration {@link Enumeration} * @return ArrayList对象 * @since 3.0.8 @@ -1204,6 +1209,36 @@ public class CollUtil { return Convert.toList(elementType, fieldValues); } + /** + * 字段值与列表值对应的Map,常用于元素对象中有唯一ID时需要按照这个ID查找对象的情况
+ * 例如:车牌号 =》车 + * + * @param 字段名对应值得类型,不确定请使用Object + * @param 对象类型 + * @param iterable 对象列表 + * @param fieldName 字段名(会通过反射获取其值) + * @return 某个字段值与对象对应Map + * @since 5.0.6 + */ + public static Map fieldValueMap(Iterable iterable, String fieldName) { + return IterUtil.fieldValueMap(null == iterable ? null : iterable.iterator(), fieldName); + } + + /** + * 两个字段值组成新的Map + * + * @param 字段名对应值得类型,不确定请使用Object + * @param 值类型,不确定使用Object + * @param iterable 对象列表 + * @param fieldNameForKey 做为键的字段名(会通过反射获取其值) + * @param fieldNameForValue 做为值的字段名(会通过反射获取其值) + * @return 某个字段值与对象对应Map + * @since 5.0.6 + */ + public static Map fieldValueAsMap(Iterable iterable, String fieldNameForKey, String fieldNameForValue) { + return IterUtil.fieldValueAsMap(null == iterable ? null : iterable.iterator(), fieldNameForKey, fieldNameForValue); + } + /** * 查找第一个匹配元素对象 * @@ -1316,13 +1351,13 @@ public class CollUtil { * 获取匹配规则定义中匹配到元素的所有位置
* 此方法对于某些无序集合的位置信息,以转换为数组后的位置为准。 * - * @param 元素类型 + * @param 元素类型 * @param collection 集合 - * @param matcher 匹配器,为空则全部匹配 + * @param matcher 匹配器,为空则全部匹配 * @return 位置数组 * @since 5.2.5 */ - public static int[] indexOfAll(Collection collection, Matcher matcher){ + public static int[] indexOfAll(Collection collection, Matcher matcher) { final List indexList = new ArrayList<>(); if (null != collection) { int index = 0; @@ -1718,6 +1753,38 @@ public class CollUtil { return MapUtil.toMapList(listMap); } + /** + * 集合转换为Map,转换规则为:
+ * 按照keyFunc函数规则根据元素对象生成Key,元素作为值 + * + * @param Map键类型 + * @param Map值类型 + * @param values 数据列表 + * @param keyFunc 生成key的函数 + * @return 生成的map + * @since 5.2.6 + */ + public static Map toMap(Iterable values, Map map, Func1 keyFunc) { + return IterUtil.toMap(null == values ? null : values.iterator(), map, keyFunc); + } + + /** + * 集合转换为Map,转换规则为:
+ * 按照keyFunc函数规则根据元素对象生成Key,按照valueFunc函数规则根据元素对象生成value组成新的Map + * + * @param Map键类型 + * @param Map值类型 + * @param 元素类型 + * @param values 数据列表 + * @param map Map对象,转换后的键值对加入此Map,通过传入此对象自定义Map类型 + * @param keyFunc 生成key的函数 + * @return 生成的map + * @since 5.2.6 + */ + public static Map toMap(Iterable values, Map map, Func1 keyFunc, Func1 valueFunc) { + return IterUtil.toMap(null == values ? null : values.iterator(), map, keyFunc, valueFunc); + } + /** * 将指定对象全部加入到集合中
* 提供的对象如果为集合类型,会自动转换为目标元素类型
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java index 9de7ce377..fd5957767 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java @@ -1,17 +1,24 @@ package cn.hutool.core.collection; +import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Filter; +import cn.hutool.core.lang.func.Func1; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; -import java.util.*; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; /** * {@link Iterable} 和 {@link Iterator} 相关工具类 - * + * * @author Looly * @since 3.1.0 */ @@ -19,7 +26,7 @@ public class IterUtil { /** * Iterable是否为空 - * + * * @param iterable Iterable对象 * @return 是否为空 */ @@ -29,7 +36,7 @@ public class IterUtil { /** * Iterator是否为空 - * + * * @param Iterator Iterator对象 * @return 是否为空 */ @@ -39,7 +46,7 @@ public class IterUtil { /** * Iterable是否为空 - * + * * @param iterable Iterable对象 * @return 是否为空 */ @@ -49,7 +56,7 @@ public class IterUtil { /** * Iterator是否为空 - * + * * @param Iterator Iterator对象 * @return 是否为空 */ @@ -59,7 +66,7 @@ public class IterUtil { /** * 是否包含{@code null}元素 - * + * * @param iter 被检查的{@link Iterable}对象,如果为{@code null} 返回true * @return 是否包含{@code null}元素 */ @@ -69,7 +76,7 @@ public class IterUtil { /** * 是否包含{@code null}元素 - * + * * @param iter 被检查的{@link Iterator}对象,如果为{@code null} 返回true * @return 是否包含{@code null}元素 */ @@ -88,7 +95,7 @@ public class IterUtil { /** * 是否全部元素为null - * + * * @param iter iter 被检查的{@link Iterable}对象,如果为{@code null} 返回true * @return 是否全部元素为null * @since 3.3.0 @@ -99,7 +106,7 @@ public class IterUtil { /** * 是否全部元素为null - * + * * @param iter iter 被检查的{@link Iterator}对象,如果为{@code null} 返回true * @return 是否全部元素为null * @since 3.3.0 @@ -124,11 +131,13 @@ public class IterUtil { * a: 1
* b: 1
* c: 3
- * - * @param 集合元素类型 + * + * @param 集合元素类型 * @param iter {@link Iterable},如果为null返回一个空的Map * @return {@link Map} + * @deprecated 如果对象同时实现Iterable和Iterator接口会产生歧义,请使用CollUtil.countMap */ + @Deprecated public static Map countMap(Iterable iter) { return countMap(null == iter ? null : iter.iterator()); } @@ -140,8 +149,8 @@ public class IterUtil { * a: 1
* b: 1
* c: 3
- * - * @param 集合元素类型 + * + * @param 集合元素类型 * @param iter {@link Iterator},如果为null返回一个空的Map * @return {@link Map} */ @@ -166,14 +175,16 @@ public class IterUtil { /** * 字段值与列表值对应的Map,常用于元素对象中有唯一ID时需要按照这个ID查找对象的情况
* 例如:车牌号 =》车 - * - * @param 字段名对应值得类型,不确定请使用Object - * @param 对象类型 - * @param iter 对象列表 + * + * @param 字段名对应值得类型,不确定请使用Object + * @param 对象类型 + * @param iter 对象列表 * @param fieldName 字段名(会通过反射获取其值) * @return 某个字段值与对象对应Map * @since 4.0.4 + * @deprecated 如果对象同时实现Iterable和Iterator接口会产生歧义,请使用CollUtil.fieldValueMap */ + @Deprecated public static Map fieldValueMap(Iterable iter, String fieldName) { return fieldValueMap(null == iter ? null : iter.iterator(), fieldName); } @@ -181,71 +192,60 @@ public class IterUtil { /** * 字段值与列表值对应的Map,常用于元素对象中有唯一ID时需要按照这个ID查找对象的情况
* 例如:车牌号 =》车 - * - * @param 字段名对应值得类型,不确定请使用Object - * @param 对象类型 - * @param iter 对象列表 + * + * @param 字段名对应值得类型,不确定请使用Object + * @param 对象类型 + * @param iter 对象列表 * @param fieldName 字段名(会通过反射获取其值) * @return 某个字段值与对象对应Map * @since 4.0.4 */ @SuppressWarnings("unchecked") public static Map fieldValueMap(Iterator iter, String fieldName) { - final Map result = new HashMap<>(); - if (null != iter) { - V value; - while (iter.hasNext()) { - value = iter.next(); - result.put((K) ReflectUtil.getFieldValue(value, fieldName), value); - } - } - return result; + return toMap(iter, new HashMap<>(), (value)->(K)ReflectUtil.getFieldValue(value, fieldName)); } - + /** * 两个字段值组成新的Map - * - * @param 字段名对应值得类型,不确定请使用Object - * @param 值类型,不确定使用Object - * @param iterable 对象列表 - * @param fieldNameForKey 做为键的字段名(会通过反射获取其值) + * + * @param 字段名对应值得类型,不确定请使用Object + * @param 值类型,不确定使用Object + * @param iterable 对象列表 + * @param fieldNameForKey 做为键的字段名(会通过反射获取其值) * @param fieldNameForValue 做为值的字段名(会通过反射获取其值) * @return 某个字段值与对象对应Map * @since 4.6.2 + * @deprecated 如果对象同时实现Iterable和Iterator接口会产生歧义,请使用CollUtil.fieldValueMap */ + @Deprecated public static Map fieldValueAsMap(Iterable iterable, String fieldNameForKey, String fieldNameForValue) { return fieldValueAsMap(null == iterable ? null : iterable.iterator(), fieldNameForKey, fieldNameForValue); } /** * 两个字段值组成新的Map - * - * @param 字段名对应值得类型,不确定请使用Object - * @param 值类型,不确定使用Object - * @param iter 对象列表 - * @param fieldNameForKey 做为键的字段名(会通过反射获取其值) + * + * @param 字段名对应值得类型,不确定请使用Object + * @param 值类型,不确定使用Object + * @param iter 对象列表 + * @param fieldNameForKey 做为键的字段名(会通过反射获取其值) * @param fieldNameForValue 做为值的字段名(会通过反射获取其值) * @return 某个字段值与对象对应Map * @since 4.0.10 */ @SuppressWarnings("unchecked") public static Map fieldValueAsMap(Iterator iter, String fieldNameForKey, String fieldNameForValue) { - final Map result = new HashMap<>(); - if (null != iter) { - Object value; - while (iter.hasNext()) { - value = iter.next(); - result.put((K) ReflectUtil.getFieldValue(value, fieldNameForKey), (V) ReflectUtil.getFieldValue(value, fieldNameForValue)); - } - } - return result; + return toMap(iter, new HashMap<>(), + (value)->(K)ReflectUtil.getFieldValue(value, fieldNameForKey), + (value)->(V)ReflectUtil.getFieldValue(value, fieldNameForValue) + ); } - + /** * 获取指定Bean列表中某个字段,生成新的列表 - * - * @param 对象类型 - * @param iterable 对象列表 + * + * @param 对象类型 + * @param iterable 对象列表 * @param fieldName 字段名(会通过反射获取其值) * @return 某个字段值与对象对应Map * @since 4.6.2 @@ -256,9 +256,9 @@ public class IterUtil { /** * 获取指定Bean列表中某个字段,生成新的列表 - * - * @param 对象类型 - * @param iter 对象列表 + * + * @param 对象类型 + * @param iter 对象列表 * @param fieldName 字段名(会通过反射获取其值) * @return 某个字段值与对象对应Map * @since 4.0.10 @@ -277,27 +277,29 @@ public class IterUtil { /** * 以 conjunction 为分隔符将集合转换为字符串 - * - * @param 集合元素类型 - * @param iterable {@link Iterable} + * + * @param 集合元素类型 + * @param iterable {@link Iterable} * @param conjunction 分隔符 * @return 连接后的字符串 + * @deprecated 如果对象同时实现Iterable和Iterator接口会产生歧义,请使用CollUtil.join */ + @Deprecated public static String join(Iterable iterable, CharSequence conjunction) { if (null == iterable) { return null; } return join(iterable.iterator(), conjunction); } - + /** * 以 conjunction 为分隔符将集合转换为字符串 - * - * @param 集合元素类型 - * @param iterable {@link Iterable} + * + * @param 集合元素类型 + * @param iterable {@link Iterable} * @param conjunction 分隔符 - * @param prefix 每个元素添加的前缀,null表示不添加 - * @param suffix 每个元素添加的后缀,null表示不添加 + * @param prefix 每个元素添加的前缀,null表示不添加 + * @param suffix 每个元素添加的后缀,null表示不添加 * @return 连接后的字符串 * @since 4.0.10 */ @@ -307,13 +309,13 @@ public class IterUtil { } return join(iterable.iterator(), conjunction, prefix, suffix); } - + /** * 以 conjunction 为分隔符将集合转换为字符串
* 如果集合元素为数组、{@link Iterable}或{@link Iterator},则递归组合其为字符串 - * - * @param 集合元素类型 - * @param iterator 集合 + * + * @param 集合元素类型 + * @param iterator 集合 * @param conjunction 分隔符 * @return 连接后的字符串 */ @@ -324,12 +326,12 @@ public class IterUtil { /** * 以 conjunction 为分隔符将集合转换为字符串
* 如果集合元素为数组、{@link Iterable}或{@link Iterator},则递归组合其为字符串 - * - * @param 集合元素类型 - * @param iterator 集合 + * + * @param 集合元素类型 + * @param iterator 集合 * @param conjunction 分隔符 - * @param prefix 每个元素添加的前缀,null表示不添加 - * @param suffix 每个元素添加的后缀,null表示不添加 + * @param prefix 每个元素添加的前缀,null表示不添加 + * @param suffix 每个元素添加的后缀,null表示不添加 * @return 连接后的字符串 * @since 4.0.10 */ @@ -364,9 +366,9 @@ public class IterUtil { /** * 将Entry集合转换为HashMap - * - * @param 键类型 - * @param 值类型 + * + * @param 键类型 + * @param 值类型 * @param entryIter entry集合 * @return Map */ @@ -379,15 +381,15 @@ public class IterUtil { } return map; } - + /** * 将键列表和值列表转换为Map
* 以键为准,值与键位置需对应。如果键元素数多于值元素,多余部分值用null代替。
* 如果值多于键,忽略多余的值。 - * - * @param 键类型 - * @param 值类型 - * @param keys 键列表 + * + * @param 键类型 + * @param 值类型 + * @param keys 键列表 * @param values 值列表 * @return 标题内容Map * @since 3.1.0 @@ -400,11 +402,11 @@ public class IterUtil { * 将键列表和值列表转换为Map
* 以键为准,值与键位置需对应。如果键元素数多于值元素,多余部分值用null代替。
* 如果值多于键,忽略多余的值。 - * - * @param 键类型 - * @param 值类型 - * @param keys 键列表 - * @param values 值列表 + * + * @param 键类型 + * @param 值类型 + * @param keys 键列表 + * @param values 值列表 * @param isOrder 是否有序 * @return 标题内容Map * @since 4.1.12 @@ -412,15 +414,15 @@ public class IterUtil { public static Map toMap(Iterable keys, Iterable values, boolean isOrder) { return toMap(null == keys ? null : keys.iterator(), null == values ? null : values.iterator(), isOrder); } - + /** * 将键列表和值列表转换为Map
* 以键为准,值与键位置需对应。如果键元素数多于值元素,多余部分值用null代替。
* 如果值多于键,忽略多余的值。 - * - * @param 键类型 - * @param 值类型 - * @param keys 键列表 + * + * @param 键类型 + * @param 值类型 + * @param keys 键列表 * @param values 值列表 * @return 标题内容Map * @since 3.1.0 @@ -433,11 +435,11 @@ public class IterUtil { * 将键列表和值列表转换为Map
* 以键为准,值与键位置需对应。如果键元素数多于值元素,多余部分值用null代替。
* 如果值多于键,忽略多余的值。 - * - * @param 键类型 - * @param 值类型 - * @param keys 键列表 - * @param values 值列表 + * + * @param 键类型 + * @param 值类型 + * @param keys 键列表 + * @param values 值列表 * @param isOrder 是否有序 * @return 标题内容Map * @since 4.1.12 @@ -455,14 +457,14 @@ public class IterUtil { /** * Iterator转List
* 不判断,直接生成新的List - * - * @param 元素类型 + * + * @param 元素类型 * @param iter {@link Iterator} * @return List * @since 4.0.6 */ public static List toList(Iterable iter) { - if(null == iter) { + if (null == iter) { return null; } return toList(iter.iterator()); @@ -471,8 +473,8 @@ public class IterUtil { /** * Iterator转List
* 不判断,直接生成新的List - * - * @param 元素类型 + * + * @param 元素类型 * @param iter {@link Iterator} * @return List * @since 4.0.6 @@ -489,9 +491,9 @@ public class IterUtil { * Enumeration转换为Iterator *

* Adapt the specified Enumeration to the Iterator interface - * + * * @param 集合元素类型 - * @param e {@link Enumeration} + * @param e {@link Enumeration} * @return {@link Iterator} */ public static Iterator asIterator(Enumeration e) { @@ -500,8 +502,8 @@ public class IterUtil { /** * {@link Iterator} 转为 {@link Iterable} - * - * @param 元素类型 + * + * @param 元素类型 * @param iter {@link Iterator} * @return {@link Iterable} */ @@ -511,8 +513,8 @@ public class IterUtil { /** * 获取集合的第一个元素 - * - * @param 集合元素类型 + * + * @param 集合元素类型 * @param iterable {@link Iterable} * @return 第一个元素 */ @@ -525,8 +527,8 @@ public class IterUtil { /** * 获取集合的第一个元素 - * - * @param 集合元素类型 + * + * @param 集合元素类型 * @param iterator {@link Iterator} * @return 第一个元素 */ @@ -540,7 +542,7 @@ public class IterUtil { /** * 获得{@link Iterable}对象的元素类型(通过第一个非空元素判断)
* 注意,此方法至少会调用多次next方法 - * + * * @param iterable {@link Iterable} * @return 元素类型,当列表为空或元素全部为null时,返回null */ @@ -555,7 +557,7 @@ public class IterUtil { /** * 获得{@link Iterator}对象的元素类型(通过第一个非空元素判断)
* 注意,此方法至少会调用多次next方法 - * + * * @param iterator {@link Iterator} * @return 元素类型,当列表为空或元素全部为null时,返回null */ @@ -569,42 +571,42 @@ public class IterUtil { } return null; } - + /** * 过滤集合,此方法在原集合上直接修改
* 通过实现Filter接口,完成元素的过滤,这个Filter实现可以实现以下功能: - * + * *

 	 * 1、过滤出需要的对象,{@link Filter#accept(Object)}方法返回false的对象将被使用{@link Iterator#remove()}方法移除
 	 * 
- * - * @param 集合类型 - * @param 集合元素类型 - * @param iter 集合 + * + * @param 集合类型 + * @param 集合元素类型 + * @param iter 集合 * @param filter 过滤器接口 * @return 编辑后的集合 * @since 4.6.5 */ public static , E> T filter(T iter, Filter filter) { - if(null == iter) { + if (null == iter) { return null; } - + filter(iter.iterator(), filter); - + return iter; } - + /** * 过滤集合,此方法在原集合上直接修改
* 通过实现Filter接口,完成元素的过滤,这个Filter实现可以实现以下功能: - * + * *
 	 * 1、过滤出需要的对象,{@link Filter#accept(Object)}方法返回false的对象将被使用{@link Iterator#remove()}方法移除
 	 * 
- * - * @param 集合元素类型 - * @param iter 集合 + * + * @param 集合元素类型 + * @param iter 集合 * @param filter 过滤器接口 * @return 编辑后的集合 * @since 4.6.5 @@ -614,11 +616,61 @@ public class IterUtil { return iter; } - while(iter.hasNext()) { - if(false == filter.accept(iter.next())) { + while (iter.hasNext()) { + if (false == filter.accept(iter.next())) { iter.remove(); } } return iter; } + + /** + * Iterator转换为Map,转换规则为:
+ * 按照keyFunc函数规则根据元素对象生成Key,元素作为值 + * + * @param Map键类型 + * @param Map值类型 + * @param iterator 数据列表 + * @param map Map对象,转换后的键值对加入此Map,通过传入此对象自定义Map类型 + * @param keyFunc 生成key的函数 + * @return 生成的map + * @since 5.2.6 + */ + public static Map toMap(Iterator iterator, Map map, Func1 keyFunc) { + return toMap(iterator, map, keyFunc, (value) -> value); + } + + /** + * 集合转换为Map,转换规则为:
+ * 按照keyFunc函数规则根据元素对象生成Key,按照valueFunc函数规则根据元素对象生成value组成新的Map + * + * @param Map键类型 + * @param Map值类型 + * @param 元素类型 + * @param iterator 数据列表 + * @param map Map对象,转换后的键值对加入此Map,通过传入此对象自定义Map类型 + * @param keyFunc 生成key的函数 + * @return 生成的map + * @since 5.2.6 + */ + public static Map toMap(Iterator iterator, Map map, Func1 keyFunc, Func1 valueFunc) { + if (null == iterator) { + return map; + } + + if (null == map) { + map = MapUtil.newHashMap(true); + } + + E element; + while (iterator.hasNext()) { + element = iterator.next(); + try { + map.put(keyFunc.call(element), valueFunc.call(element)); + } catch (Exception e) { + throw new UtilException(e); + } + } + return map; + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java b/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java index 36e503915..74a355455 100644 --- a/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java @@ -12,6 +12,7 @@ import cn.hutool.core.util.StrUtil; import java.io.IOException; import java.io.OutputStream; import java.net.DatagramSocket; +import java.net.HttpCookie; import java.net.IDN; import java.net.Inet4Address; import java.net.Inet6Address; @@ -29,6 +30,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import java.util.TreeSet; @@ -647,7 +649,7 @@ public class NetUtil { if (ip != null && ip.indexOf(",") > 0) { final String[] ips = ip.trim().split(","); for (String subIp : ips) { - if (false == isUnknow(subIp)) { + if (false == isUnknown(subIp)) { ip = subIp; break; } @@ -662,8 +664,20 @@ public class NetUtil { * @param checkString 被检测的字符串 * @return 是否未知 * @since 4.4.1 + * @deprecated 拼写错误,请使用{@link #isUnknown(String)} */ public static boolean isUnknow(String checkString) { + return isUnknown(checkString); + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关
+ * + * @param checkString 被检测的字符串 + * @return 是否未知 + * @since 5.2.6 + */ + public static boolean isUnknown(String checkString) { return StrUtil.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); } @@ -692,6 +706,19 @@ public class NetUtil { } } + /** + * 解析Cookie信息 + * + * @param cookieStr Cookie字符串 + * @return cookie字符串 + * @since 5.2.6 + */ + public static List parseCookies(String cookieStr){ + if(StrUtil.isBlank(cookieStr)){ + return CollUtil.newArrayList(); + } + return HttpCookie.parse(cookieStr); + } // ----------------------------------------------------------------------------------------- Private method start /** diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java index d473f2f58..85427b99a 100644 --- a/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java @@ -277,6 +277,32 @@ public class CollUtilTest { Assert.assertEquals("张三", list.get(2).getName()); } + @Test + public void fieldValueMapTest() { + List list = CollUtil.newArrayList(new TestBean("张三", 12, DateUtil.parse("2018-05-01")), // + new TestBean("李四", 13, DateUtil.parse("2018-03-01")), // + new TestBean("王五", 12, DateUtil.parse("2018-04-01"))// + ); + + final Map map = CollUtil.fieldValueMap(list, "name"); + Assert.assertEquals("李四", map.get("李四").getName()); + Assert.assertEquals("王五", map.get("王五").getName()); + Assert.assertEquals("张三", map.get("张三").getName()); + } + + @Test + public void fieldValueAsMapTest() { + List list = CollUtil.newArrayList(new TestBean("张三", 12, DateUtil.parse("2018-05-01")), // + new TestBean("李四", 13, DateUtil.parse("2018-03-01")), // + new TestBean("王五", 14, DateUtil.parse("2018-04-01"))// + ); + + final Map map = CollUtil.fieldValueAsMap(list, "name", "age"); + Assert.assertEquals(new Integer(12), map.get("张三")); + Assert.assertEquals(new Integer(13), map.get("李四")); + Assert.assertEquals(new Integer(14), map.get("王五")); + } + public static class TestBean { private String name; private int age; @@ -600,4 +626,25 @@ public class CollUtilTest { Assert.assertEquals(3, map.get("c").intValue()); Assert.assertEquals(4, map.get("d").intValue()); } + + @Test + public void toMapTest(){ + Collection keys = CollUtil.newArrayList("a", "b", "c", "d"); + final Map map = CollUtil.toMap(keys, new HashMap<>(), (value)->"key" + value); + Assert.assertEquals("a", map.get("keya")); + Assert.assertEquals("b", map.get("keyb")); + Assert.assertEquals("c", map.get("keyc")); + Assert.assertEquals("d", map.get("keyd")); + } + + @Test + public void countMapTest() { + ArrayList list = CollUtil.newArrayList("a", "b", "c", "c", "a", "b", "d"); + Map countMap = CollUtil.countMap(list); + + Assert.assertEquals(Integer.valueOf(2), countMap.get("a")); + Assert.assertEquals(Integer.valueOf(2), countMap.get("b")); + Assert.assertEquals(Integer.valueOf(2), countMap.get("c")); + Assert.assertEquals(Integer.valueOf(1), countMap.get("d")); + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/IterUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/IterUtilTest.java index 11351f461..f5eabd172 100644 --- a/hutool-core/src/test/java/cn/hutool/core/collection/IterUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/collection/IterUtilTest.java @@ -1,11 +1,11 @@ package cn.hutool.core.collection; -import java.util.ArrayList; -import java.util.Map; - import org.junit.Assert; import org.junit.Test; +import java.util.ArrayList; +import java.util.Map; + /** * {@link IterUtil} 单元测试 * @author looly @@ -13,17 +13,6 @@ import org.junit.Test; */ public class IterUtilTest { - @Test - public void countMapTest() { - ArrayList list = CollUtil.newArrayList("a", "b", "c", "c", "a", "b", "d"); - Map countMap = IterUtil.countMap(list); - - Assert.assertEquals(Integer.valueOf(2), countMap.get("a")); - Assert.assertEquals(Integer.valueOf(2), countMap.get("b")); - Assert.assertEquals(Integer.valueOf(2), countMap.get("c")); - Assert.assertEquals(Integer.valueOf(1), countMap.get("d")); - } - @Test public void fieldValueMapTest() { ArrayList carList = CollUtil.newArrayList(new Car("123", "大众"), new Car("345", "奔驰"), new Car("567", "路虎")); diff --git a/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java index 32a16a7ad..0d6b8a87e 100644 --- a/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java @@ -6,7 +6,9 @@ import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; +import java.net.HttpCookie; import java.net.InetAddress; +import java.util.List; /** * NetUtil单元测试 @@ -57,4 +59,18 @@ public class NetUtilTest { public void isUsableLocalPortTest(){ Assert.assertTrue(NetUtil.isUsableLocalPort(80)); } + + @Test + public void parseCookiesTest(){ + String cookieStr = "cookieName=\"cookieValue\";Path=\"/\";Domain=\"cookiedomain.com\""; + final List httpCookies = NetUtil.parseCookies(cookieStr); + Assert.assertEquals(1, httpCookies.size()); + + final HttpCookie httpCookie = httpCookies.get(0); + Assert.assertEquals(0, httpCookie.getVersion()); + Assert.assertEquals("cookieName", httpCookie.getName()); + Assert.assertEquals("cookieValue", httpCookie.getValue()); + Assert.assertEquals("/", httpCookie.getPath()); + Assert.assertEquals("cookiedomain.com", httpCookie.getDomain()); + } } diff --git a/hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java index 70afe80fb..f91530be4 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java @@ -3,11 +3,14 @@ package cn.hutool.extra.servlet; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.bean.copier.ValueProvider; +import cn.hutool.core.collection.ArrayIter; +import cn.hutool.core.collection.IterUtil; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.map.CaseInsensitiveMap; +import cn.hutool.core.net.NetUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.ObjectUtil; @@ -227,13 +230,13 @@ public class ServletUtil { String ip; for (String header : headerNames) { ip = request.getHeader(header); - if (false == isUnknow(ip)) { - return getMultistageReverseProxyIp(ip); + if (false == NetUtil.isUnknown(ip)) { + return NetUtil.getMultistageReverseProxyIp(ip); } } ip = request.getRemoteAddr(); - return getMultistageReverseProxyIp(ip); + return NetUtil.getMultistageReverseProxyIp(ip); } /** @@ -415,14 +418,10 @@ public class ServletUtil { * @return Cookie map */ public static Map readCookieMap(HttpServletRequest httpServletRequest) { - final Map cookieMap = new CaseInsensitiveMap<>(); - final Cookie[] cookies = httpServletRequest.getCookies(); - if (null != cookies) { - for (Cookie cookie : cookies) { - cookieMap.put(cookie.getName(), cookie); - } - } - return cookieMap; + return IterUtil.toMap( + new ArrayIter<>(httpServletRequest.getCookies()), + new CaseInsensitiveMap<>(), + Cookie::getName); } /** @@ -614,37 +613,4 @@ public class ServletUtil { } } // --------------------------------------------------------- Response end - - // --------------------------------------------------------- Private methd start - /** - * 从多级反向代理中获得第一个非unknown IP地址 - * - * @param ip 获得的IP地址 - * @return 第一个非unknown IP地址 - */ - private static String getMultistageReverseProxyIp(String ip) { - // 多级反向代理检测 - if (ip != null && ip.indexOf(",") > 0) { - final String[] ips = ip.trim().split(","); - for (String subIp : ips) { - if (false == isUnknow(subIp)) { - ip = subIp; - break; - } - } - } - return ip; - } - - /** - * 检测给定字符串是否为未知,多用于检测HTTP请求相关
- * - * @param checkString 被检测的字符串 - * @return 是否未知 - */ - private static boolean isUnknow(String checkString) { - return StrUtil.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); - } - // --------------------------------------------------------- Private methd end - } diff --git a/hutool-http/src/main/java/cn/hutool/http/server/HttpRequest.java b/hutool-http/src/main/java/cn/hutool/http/server/HttpRequest.java index e10b50507..116b83560 100644 --- a/hutool-http/src/main/java/cn/hutool/http/server/HttpRequest.java +++ b/hutool-http/src/main/java/cn/hutool/http/server/HttpRequest.java @@ -1,6 +1,10 @@ package cn.hutool.http.server; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.IoUtil; +import cn.hutool.core.map.CaseInsensitiveMap; +import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.Header; @@ -12,8 +16,11 @@ import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import java.io.InputStream; +import java.net.HttpCookie; import java.net.URI; import java.nio.charset.Charset; +import java.util.Collection; +import java.util.Map; /** * Http请求对象,对{@link HttpExchange}封装 @@ -25,6 +32,8 @@ public class HttpRequest { private final HttpExchange httpExchange; + private Map cookieCache; + /** * 构造 * @@ -97,6 +106,16 @@ public class HttpRequest { return this.httpExchange.getRequestHeaders(); } + /** + * 获得请求header中的信息 + * + * @param headerKey 头信息的KEY + * @return header值 + */ + public String getHeader(Header headerKey) { + return getHeader(headerKey.toString()); + } + /** * 获得请求header中的信息 * @@ -122,13 +141,22 @@ public class HttpRequest { return null; } + /** + * 获取Content-Type头信息 + * + * @return Content-Type头信息 + */ + public String getContentType() { + return getHeader(Header.USER_AGENT); + } + /** * 获得User-Agent * * @return User-Agent字符串 */ public String getUserAgentStr() { - return getHeader("User-Agent"); + return getHeader(Header.USER_AGENT); } /** @@ -140,6 +168,49 @@ public class HttpRequest { return UserAgentUtil.parse(getUserAgentStr()); } + /** + * 获得Cookie信息字符串 + * + * @return cookie字符串 + */ + public String getCookiesStr() { + return getHeader(Header.COOKIE); + } + + /** + * 获得Cookie信息列表 + * + * @return Cookie信息列表 + */ + public Collection getCookies() { + return getCookieMap().values(); + } + + /** + * 获得Cookie信息Map,键为Cookie名,值为HttpCookie对象 + * + * @return Cookie信息Map + */ + public Map getCookieMap() { + if (null == this.cookieCache) { + cookieCache = CollUtil.toMap( + NetUtil.parseCookies(getCookiesStr()), + new CaseInsensitiveMap<>(), + HttpCookie::getName); + } + return cookieCache; + } + + /** + * 获得指定Cookie名对应的HttpCookie对象 + * + * @param cookieName Cookie名 + * @return HttpCookie对象 + */ + public HttpCookie getCookie(String cookieName) { + return getCookieMap().get(cookieName); + } + /** * 获取请求体的流,流中可以读取请求内容,包括请求表单数据或文件上传数据 * @@ -156,7 +227,7 @@ public class HttpRequest { * @return 请求 */ public String getBody() { - final String contentType = getHeader(Header.CONTENT_TYPE.toString()); + final String contentType = getContentType(); final String charsetStr = HttpUtil.getCharset(contentType); final Charset charset = CharsetUtil.parse(charsetStr, CharsetUtil.CHARSET_UTF_8); @@ -189,11 +260,66 @@ public class HttpRequest { return false; } - final String contentType = getHeader(Header.CONTENT_TYPE.toString()); + final String contentType = getContentType(); if (StrUtil.isBlank(contentType)) { return false; } return contentType.toLowerCase().startsWith("multipart/"); } + + /** + * 获取客户端IP + * + *

+ * 默认检测的Header: + * + *

+	 * 1、X-Forwarded-For
+	 * 2、X-Real-IP
+	 * 3、Proxy-Client-IP
+	 * 4、WL-Proxy-Client-IP
+	 * 
+ * + *

+ * otherHeaderNames参数用于自定义检测的Header
+ * 需要注意的是,使用此方法获取的客户IP地址必须在Http服务器(例如Nginx)中配置头信息,否则容易造成IP伪造。 + *

+ * + * @param otherHeaderNames 其他自定义头文件,通常在Http服务器(例如Nginx)中配置 + * @return IP地址 + */ + public String getClientIP(String... otherHeaderNames) { + String[] headers = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"}; + if (ArrayUtil.isNotEmpty(otherHeaderNames)) { + headers = ArrayUtil.addAll(headers, otherHeaderNames); + } + + return getClientIPByHeader(headers); + } + + /** + * 获取客户端IP + * + *

+ * headerNames参数用于自定义检测的Header
+ * 需要注意的是,使用此方法获取的客户IP地址必须在Http服务器(例如Nginx)中配置头信息,否则容易造成IP伪造。 + *

+ * + * @param headerNames 自定义头,通常在Http服务器(例如Nginx)中配置 + * @return IP地址 + * @since 4.4.1 + */ + public String getClientIPByHeader(String... headerNames) { + String ip; + for (String header : headerNames) { + ip = getHeader(header); + if (false == NetUtil.isUnknown(ip)) { + return NetUtil.getMultistageReverseProxyIp(ip); + } + } + + ip = this.httpExchange.getRemoteAddress().getHostName(); + return NetUtil.getMultistageReverseProxyIp(ip); + } }