Looly 5 years ago
parent
commit
17be56a99c

+ 3 - 0
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重启报错问题

+ 80 - 13
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 <T> 元素类型
+	 * @param <T>   元素类型
 	 * @return 单差集
 	 */
 	public static <T> Collection<T> subtract(Collection<T> coll1, Collection<T> coll2) {
@@ -327,10 +328,10 @@ public class CollUtil {
 	 * @param <T>        集合元素类型
 	 * @param collection 集合
 	 * @return {@link Map}
-	 * @see IterUtil#countMap(Iterable)
+	 * @see IterUtil#countMap(Iterator)
 	 */
 	public static <T> Map<T, Integer> countMap(Iterable<T> 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 <T> String join(Iterable<T> 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 <T> String join(Iterator<T> iterator, CharSequence conjunction) {
 		return IterUtil.join(iterator, conjunction);
 	}
@@ -643,8 +648,8 @@ public class CollUtil {
 	 * 新建一个List<br>
 	 * 提供的参数为null时返回空{@link ArrayList}
 	 *
-	 * @param <T>        集合元素类型
-	 * @param isLinked   是否新建LinkedList
+	 * @param <T>         集合元素类型
+	 * @param isLinked    是否新建LinkedList
 	 * @param enumeration {@link Enumeration}
 	 * @return ArrayList对象
 	 * @since 3.0.8
@@ -707,7 +712,7 @@ public class CollUtil {
 	 * 新建一个ArrayList<br>
 	 * 提供的参数为null时返回空{@link ArrayList}
 	 *
-	 * @param <T>  集合元素类型
+	 * @param <T>      集合元素类型
 	 * @param iterator {@link Iterator}
 	 * @return ArrayList对象
 	 * @since 3.0.8
@@ -720,7 +725,7 @@ public class CollUtil {
 	 * 新建一个ArrayList<br>
 	 * 提供的参数为null时返回空{@link ArrayList}
 	 *
-	 * @param <T>        集合元素类型
+	 * @param <T>         集合元素类型
 	 * @param enumeration {@link Enumeration}
 	 * @return ArrayList对象
 	 * @since 3.0.8
@@ -1205,6 +1210,36 @@ public class CollUtil {
 	}
 
 	/**
+	 * 字段值与列表值对应的Map,常用于元素对象中有唯一ID时需要按照这个ID查找对象的情况<br>
+	 * 例如:车牌号 =》车
+	 *
+	 * @param <K>       字段名对应值得类型,不确定请使用Object
+	 * @param <V>       对象类型
+	 * @param iterable  对象列表
+	 * @param fieldName 字段名(会通过反射获取其值)
+	 * @return 某个字段值与对象对应Map
+	 * @since 5.0.6
+	 */
+	public static <K, V> Map<K, V> fieldValueMap(Iterable<V> iterable, String fieldName) {
+		return IterUtil.fieldValueMap(null == iterable ? null : iterable.iterator(), fieldName);
+	}
+
+	/**
+	 * 两个字段值组成新的Map
+	 *
+	 * @param <K>               字段名对应值得类型,不确定请使用Object
+	 * @param <V>               值类型,不确定使用Object
+	 * @param iterable          对象列表
+	 * @param fieldNameForKey   做为键的字段名(会通过反射获取其值)
+	 * @param fieldNameForValue 做为值的字段名(会通过反射获取其值)
+	 * @return 某个字段值与对象对应Map
+	 * @since 5.0.6
+	 */
+	public static <K, V> Map<K, V> fieldValueAsMap(Iterable<?> iterable, String fieldNameForKey, String fieldNameForValue) {
+		return IterUtil.fieldValueAsMap(null == iterable ? null : iterable.iterator(), fieldNameForKey, fieldNameForValue);
+	}
+
+	/**
 	 * 查找第一个匹配元素对象
 	 *
 	 * @param <T>        集合元素类型
@@ -1316,13 +1351,13 @@ public class CollUtil {
 	 * 获取匹配规则定义中匹配到元素的所有位置<br>
 	 * 此方法对于某些无序集合的位置信息,以转换为数组后的位置为准。
 	 *
-	 * @param <T> 元素类型
+	 * @param <T>        元素类型
 	 * @param collection 集合
-	 * @param matcher 匹配器,为空则全部匹配
+	 * @param matcher    匹配器,为空则全部匹配
 	 * @return 位置数组
 	 * @since 5.2.5
 	 */
-	public static <T> int[] indexOfAll(Collection<T> collection, Matcher<T> matcher){
+	public static <T> int[] indexOfAll(Collection<T> collection, Matcher<T> matcher) {
 		final List<Integer> indexList = new ArrayList<>();
 		if (null != collection) {
 			int index = 0;
@@ -1719,6 +1754,38 @@ public class CollUtil {
 	}
 
 	/**
+	 * 集合转换为Map,转换规则为:<br>
+	 * 按照keyFunc函数规则根据元素对象生成Key,元素作为值
+	 *
+	 * @param <K>     Map键类型
+	 * @param <V>     Map值类型
+	 * @param values  数据列表
+	 * @param keyFunc 生成key的函数
+	 * @return 生成的map
+	 * @since 5.2.6
+	 */
+	public static <K, V> Map<K, V> toMap(Iterable<V> values, Map<K, V> map, Func1<V, K> keyFunc) {
+		return IterUtil.toMap(null == values ? null : values.iterator(), map, keyFunc);
+	}
+
+	/**
+	 * 集合转换为Map,转换规则为:<br>
+	 * 按照keyFunc函数规则根据元素对象生成Key,按照valueFunc函数规则根据元素对象生成value组成新的Map
+	 *
+	 * @param <K>     Map键类型
+	 * @param <V>     Map值类型
+	 * @param <E>     元素类型
+	 * @param values  数据列表
+	 * @param map     Map对象,转换后的键值对加入此Map,通过传入此对象自定义Map类型
+	 * @param keyFunc 生成key的函数
+	 * @return 生成的map
+	 * @since 5.2.6
+	 */
+	public static <K, V, E> Map<K, V> toMap(Iterable<E> values, Map<K, V> map, Func1<E, K> keyFunc, Func1<E, V> valueFunc) {
+		return IterUtil.toMap(null == values ? null : values.iterator(), map, keyFunc, valueFunc);
+	}
+
+	/**
 	 * 将指定对象全部加入到集合中<br>
 	 * 提供的对象如果为集合类型,会自动转换为目标元素类型<br>
 	 *

+ 182 - 130
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<br>
 	 * b: 1<br>
 	 * c: 3<br>
-	 * 
-	 * @param <T> 集合元素类型
+	 *
+	 * @param <T>  集合元素类型
 	 * @param iter {@link Iterable},如果为null返回一个空的Map
 	 * @return {@link Map}
+	 * @deprecated 如果对象同时实现Iterable和Iterator接口会产生歧义,请使用CollUtil.countMap
 	 */
+	@Deprecated
 	public static <T> Map<T, Integer> countMap(Iterable<T> iter) {
 		return countMap(null == iter ? null : iter.iterator());
 	}
@@ -140,8 +149,8 @@ public class IterUtil {
 	 * a: 1<br>
 	 * b: 1<br>
 	 * c: 3<br>
-	 * 
-	 * @param <T> 集合元素类型
+	 *
+	 * @param <T>  集合元素类型
 	 * @param iter {@link Iterator},如果为null返回一个空的Map
 	 * @return {@link Map}
 	 */
@@ -166,14 +175,16 @@ public class IterUtil {
 	/**
 	 * 字段值与列表值对应的Map,常用于元素对象中有唯一ID时需要按照这个ID查找对象的情况<br>
 	 * 例如:车牌号 =》车
-	 * 
-	 * @param <K> 字段名对应值得类型,不确定请使用Object
-	 * @param <V> 对象类型
-	 * @param iter 对象列表
+	 *
+	 * @param <K>       字段名对应值得类型,不确定请使用Object
+	 * @param <V>       对象类型
+	 * @param iter      对象列表
 	 * @param fieldName 字段名(会通过反射获取其值)
 	 * @return 某个字段值与对象对应Map
 	 * @since 4.0.4
+	 * @deprecated 如果对象同时实现Iterable和Iterator接口会产生歧义,请使用CollUtil.fieldValueMap
 	 */
+	@Deprecated
 	public static <K, V> Map<K, V> fieldValueMap(Iterable<V> iter, String fieldName) {
 		return fieldValueMap(null == iter ? null : iter.iterator(), fieldName);
 	}
@@ -181,71 +192,60 @@ public class IterUtil {
 	/**
 	 * 字段值与列表值对应的Map,常用于元素对象中有唯一ID时需要按照这个ID查找对象的情况<br>
 	 * 例如:车牌号 =》车
-	 * 
-	 * @param <K> 字段名对应值得类型,不确定请使用Object
-	 * @param <V> 对象类型
-	 * @param iter 对象列表
+	 *
+	 * @param <K>       字段名对应值得类型,不确定请使用Object
+	 * @param <V>       对象类型
+	 * @param iter      对象列表
 	 * @param fieldName 字段名(会通过反射获取其值)
 	 * @return 某个字段值与对象对应Map
 	 * @since 4.0.4
 	 */
 	@SuppressWarnings("unchecked")
 	public static <K, V> Map<K, V> fieldValueMap(Iterator<V> iter, String fieldName) {
-		final Map<K, V> 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 <K> 字段名对应值得类型,不确定请使用Object
-	 * @param <V> 值类型,不确定使用Object
-	 * @param iterable 对象列表
-	 * @param fieldNameForKey 做为键的字段名(会通过反射获取其值)
+	 *
+	 * @param <K>               字段名对应值得类型,不确定请使用Object
+	 * @param <V>               值类型,不确定使用Object
+	 * @param iterable          对象列表
+	 * @param fieldNameForKey   做为键的字段名(会通过反射获取其值)
 	 * @param fieldNameForValue 做为值的字段名(会通过反射获取其值)
 	 * @return 某个字段值与对象对应Map
 	 * @since 4.6.2
+	 * @deprecated 如果对象同时实现Iterable和Iterator接口会产生歧义,请使用CollUtil.fieldValueMap
 	 */
+	@Deprecated
 	public static <K, V> Map<K, V> fieldValueAsMap(Iterable<?> iterable, String fieldNameForKey, String fieldNameForValue) {
 		return fieldValueAsMap(null == iterable ? null : iterable.iterator(), fieldNameForKey, fieldNameForValue);
 	}
 
 	/**
 	 * 两个字段值组成新的Map
-	 * 
-	 * @param <K> 字段名对应值得类型,不确定请使用Object
-	 * @param <V> 值类型,不确定使用Object
-	 * @param iter 对象列表
-	 * @param fieldNameForKey 做为键的字段名(会通过反射获取其值)
+	 *
+	 * @param <K>               字段名对应值得类型,不确定请使用Object
+	 * @param <V>               值类型,不确定使用Object
+	 * @param iter              对象列表
+	 * @param fieldNameForKey   做为键的字段名(会通过反射获取其值)
 	 * @param fieldNameForValue 做为值的字段名(会通过反射获取其值)
 	 * @return 某个字段值与对象对应Map
 	 * @since 4.0.10
 	 */
 	@SuppressWarnings("unchecked")
 	public static <K, V> Map<K, V> fieldValueAsMap(Iterator<?> iter, String fieldNameForKey, String fieldNameForValue) {
-		final Map<K, V> 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 <V> 对象类型
-	 * @param iterable 对象列表
+	 *
+	 * @param <V>       对象类型
+	 * @param iterable  对象列表
 	 * @param fieldName 字段名(会通过反射获取其值)
 	 * @return 某个字段值与对象对应Map
 	 * @since 4.6.2
@@ -256,9 +256,9 @@ public class IterUtil {
 
 	/**
 	 * 获取指定Bean列表中某个字段,生成新的列表
-	 * 
-	 * @param <V> 对象类型
-	 * @param iter 对象列表
+	 *
+	 * @param <V>       对象类型
+	 * @param iter      对象列表
 	 * @param fieldName 字段名(会通过反射获取其值)
 	 * @return 某个字段值与对象对应Map
 	 * @since 4.0.10
@@ -277,27 +277,29 @@ public class IterUtil {
 
 	/**
 	 * 以 conjunction 为分隔符将集合转换为字符串
-	 * 
-	 * @param <T> 集合元素类型
-	 * @param iterable {@link Iterable}
+	 *
+	 * @param <T>         集合元素类型
+	 * @param iterable    {@link Iterable}
 	 * @param conjunction 分隔符
 	 * @return 连接后的字符串
+	 * @deprecated 如果对象同时实现Iterable和Iterator接口会产生歧义,请使用CollUtil.join
 	 */
+	@Deprecated
 	public static <T> String join(Iterable<T> iterable, CharSequence conjunction) {
 		if (null == iterable) {
 			return null;
 		}
 		return join(iterable.iterator(), conjunction);
 	}
-	
+
 	/**
 	 * 以 conjunction 为分隔符将集合转换为字符串
-	 * 
-	 * @param <T> 集合元素类型
-	 * @param iterable {@link Iterable}
+	 *
+	 * @param <T>         集合元素类型
+	 * @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 为分隔符将集合转换为字符串<br>
 	 * 如果集合元素为数组、{@link Iterable}或{@link Iterator},则递归组合其为字符串
-	 * 
-	 * @param <T> 集合元素类型
-	 * @param iterator 集合
+	 *
+	 * @param <T>         集合元素类型
+	 * @param iterator    集合
 	 * @param conjunction 分隔符
 	 * @return 连接后的字符串
 	 */
@@ -324,12 +326,12 @@ public class IterUtil {
 	/**
 	 * 以 conjunction 为分隔符将集合转换为字符串<br>
 	 * 如果集合元素为数组、{@link Iterable}或{@link Iterator},则递归组合其为字符串
-	 * 
-	 * @param <T> 集合元素类型
-	 * @param iterator 集合
+	 *
+	 * @param <T>         集合元素类型
+	 * @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 <K> 键类型
-	 * @param <V> 值类型
+	 *
+	 * @param <K>       键类型
+	 * @param <V>       值类型
 	 * @param entryIter entry集合
 	 * @return Map
 	 */
@@ -379,15 +381,15 @@ public class IterUtil {
 		}
 		return map;
 	}
-	
+
 	/**
 	 * 将键列表和值列表转换为Map<br>
 	 * 以键为准,值与键位置需对应。如果键元素数多于值元素,多余部分值用null代替。<br>
 	 * 如果值多于键,忽略多余的值。
-	 * 
-	 * @param <K> 键类型
-	 * @param <V> 值类型
-	 * @param keys 键列表
+	 *
+	 * @param <K>    键类型
+	 * @param <V>    值类型
+	 * @param keys   键列表
 	 * @param values 值列表
 	 * @return 标题内容Map
 	 * @since 3.1.0
@@ -400,11 +402,11 @@ public class IterUtil {
 	 * 将键列表和值列表转换为Map<br>
 	 * 以键为准,值与键位置需对应。如果键元素数多于值元素,多余部分值用null代替。<br>
 	 * 如果值多于键,忽略多余的值。
-	 * 
-	 * @param <K> 键类型
-	 * @param <V> 值类型
-	 * @param keys 键列表
-	 * @param values 值列表
+	 *
+	 * @param <K>     键类型
+	 * @param <V>     值类型
+	 * @param keys    键列表
+	 * @param values  值列表
 	 * @param isOrder 是否有序
 	 * @return 标题内容Map
 	 * @since 4.1.12
@@ -412,15 +414,15 @@ public class IterUtil {
 	public static <K, V> Map<K, V> toMap(Iterable<K> keys, Iterable<V> values, boolean isOrder) {
 		return toMap(null == keys ? null : keys.iterator(), null == values ? null : values.iterator(), isOrder);
 	}
-	
+
 	/**
 	 * 将键列表和值列表转换为Map<br>
 	 * 以键为准,值与键位置需对应。如果键元素数多于值元素,多余部分值用null代替。<br>
 	 * 如果值多于键,忽略多余的值。
-	 * 
-	 * @param <K> 键类型
-	 * @param <V> 值类型
-	 * @param keys 键列表
+	 *
+	 * @param <K>    键类型
+	 * @param <V>    值类型
+	 * @param keys   键列表
 	 * @param values 值列表
 	 * @return 标题内容Map
 	 * @since 3.1.0
@@ -433,11 +435,11 @@ public class IterUtil {
 	 * 将键列表和值列表转换为Map<br>
 	 * 以键为准,值与键位置需对应。如果键元素数多于值元素,多余部分值用null代替。<br>
 	 * 如果值多于键,忽略多余的值。
-	 * 
-	 * @param <K> 键类型
-	 * @param <V> 值类型
-	 * @param keys 键列表
-	 * @param values 值列表
+	 *
+	 * @param <K>     键类型
+	 * @param <V>     值类型
+	 * @param keys    键列表
+	 * @param values  值列表
 	 * @param isOrder 是否有序
 	 * @return 标题内容Map
 	 * @since 4.1.12
@@ -455,14 +457,14 @@ public class IterUtil {
 	/**
 	 * Iterator转List<br>
 	 * 不判断,直接生成新的List
-	 * 
-	 * @param <E> 元素类型
+	 *
+	 * @param <E>  元素类型
 	 * @param iter {@link Iterator}
 	 * @return List
 	 * @since 4.0.6
 	 */
 	public static <E> List<E> toList(Iterable<E> iter) {
-		if(null == iter) {
+		if (null == iter) {
 			return null;
 		}
 		return toList(iter.iterator());
@@ -471,8 +473,8 @@ public class IterUtil {
 	/**
 	 * Iterator转List<br>
 	 * 不判断,直接生成新的List
-	 * 
-	 * @param <E> 元素类型
+	 *
+	 * @param <E>  元素类型
 	 * @param iter {@link Iterator}
 	 * @return List
 	 * @since 4.0.6
@@ -489,9 +491,9 @@ public class IterUtil {
 	 * Enumeration转换为Iterator
 	 * <p>
 	 * Adapt the specified <code>Enumeration</code> to the <code>Iterator</code> interface
-	 * 
+	 *
 	 * @param <E> 集合元素类型
-	 * @param e {@link Enumeration}
+	 * @param e   {@link Enumeration}
 	 * @return {@link Iterator}
 	 */
 	public static <E> Iterator<E> asIterator(Enumeration<E> e) {
@@ -500,8 +502,8 @@ public class IterUtil {
 
 	/**
 	 * {@link Iterator} 转为 {@link Iterable}
-	 * 
-	 * @param <E> 元素类型
+	 *
+	 * @param <E>  元素类型
 	 * @param iter {@link Iterator}
 	 * @return {@link Iterable}
 	 */
@@ -511,8 +513,8 @@ public class IterUtil {
 
 	/**
 	 * 获取集合的第一个元素
-	 * 
-	 * @param <T> 集合元素类型
+	 *
+	 * @param <T>      集合元素类型
 	 * @param iterable {@link Iterable}
 	 * @return 第一个元素
 	 */
@@ -525,8 +527,8 @@ public class IterUtil {
 
 	/**
 	 * 获取集合的第一个元素
-	 * 
-	 * @param <T> 集合元素类型
+	 *
+	 * @param <T>      集合元素类型
 	 * @param iterator {@link Iterator}
 	 * @return 第一个元素
 	 */
@@ -540,7 +542,7 @@ public class IterUtil {
 	/**
 	 * 获得{@link Iterable}对象的元素类型(通过第一个非空元素判断)<br>
 	 * 注意,此方法至少会调用多次next方法
-	 * 
+	 *
 	 * @param iterable {@link Iterable}
 	 * @return 元素类型,当列表为空或元素全部为null时,返回null
 	 */
@@ -555,7 +557,7 @@ public class IterUtil {
 	/**
 	 * 获得{@link Iterator}对象的元素类型(通过第一个非空元素判断)<br>
 	 * 注意,此方法至少会调用多次next方法
-	 * 
+	 *
 	 * @param iterator {@link Iterator}
 	 * @return 元素类型,当列表为空或元素全部为null时,返回null
 	 */
@@ -569,42 +571,42 @@ public class IterUtil {
 		}
 		return null;
 	}
-	
+
 	/**
 	 * 过滤集合,此方法在原集合上直接修改<br>
 	 * 通过实现Filter接口,完成元素的过滤,这个Filter实现可以实现以下功能:
-	 * 
+	 *
 	 * <pre>
 	 * 1、过滤出需要的对象,{@link Filter#accept(Object)}方法返回false的对象将被使用{@link Iterator#remove()}方法移除
 	 * </pre>
-	 * 
-	 * @param <T> 集合类型
-	 * @param <E> 集合元素类型
-	 * @param iter 集合
+	 *
+	 * @param <T>    集合类型
+	 * @param <E>    集合元素类型
+	 * @param iter   集合
 	 * @param filter 过滤器接口
 	 * @return 编辑后的集合
 	 * @since 4.6.5
 	 */
 	public static <T extends Iterable<E>, E> T filter(T iter, Filter<E> filter) {
-		if(null == iter) {
+		if (null == iter) {
 			return null;
 		}
-		
+
 		filter(iter.iterator(), filter);
-		
+
 		return iter;
 	}
-	
+
 	/**
 	 * 过滤集合,此方法在原集合上直接修改<br>
 	 * 通过实现Filter接口,完成元素的过滤,这个Filter实现可以实现以下功能:
-	 * 
+	 *
 	 * <pre>
 	 * 1、过滤出需要的对象,{@link Filter#accept(Object)}方法返回false的对象将被使用{@link Iterator#remove()}方法移除
 	 * </pre>
-	 * 
-	 * @param <E> 集合元素类型
-	 * @param iter 集合
+	 *
+	 * @param <E>    集合元素类型
+	 * @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,转换规则为:<br>
+	 * 按照keyFunc函数规则根据元素对象生成Key,元素作为值
+	 *
+	 * @param <K>      Map键类型
+	 * @param <V>      Map值类型
+	 * @param iterator 数据列表
+	 * @param map      Map对象,转换后的键值对加入此Map,通过传入此对象自定义Map类型
+	 * @param keyFunc  生成key的函数
+	 * @return 生成的map
+	 * @since 5.2.6
+	 */
+	public static <K, V> Map<K, V> toMap(Iterator<V> iterator, Map<K, V> map, Func1<V, K> keyFunc) {
+		return toMap(iterator, map, keyFunc, (value) -> value);
+	}
+
+	/**
+	 * 集合转换为Map,转换规则为:<br>
+	 * 按照keyFunc函数规则根据元素对象生成Key,按照valueFunc函数规则根据元素对象生成value组成新的Map
+	 *
+	 * @param <K>      Map键类型
+	 * @param <V>      Map值类型
+	 * @param <E>      元素类型
+	 * @param iterator 数据列表
+	 * @param map      Map对象,转换后的键值对加入此Map,通过传入此对象自定义Map类型
+	 * @param keyFunc  生成key的函数
+	 * @return 生成的map
+	 * @since 5.2.6
+	 */
+	public static <K, V, E> Map<K, V> toMap(Iterator<E> iterator, Map<K, V> map, Func1<E, K> keyFunc, Func1<E, V> 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;
+	}
 }

+ 28 - 1
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请求相关<br>
+	 *
+	 * @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<HttpCookie> parseCookies(String cookieStr){
+		if(StrUtil.isBlank(cookieStr)){
+			return CollUtil.newArrayList();
+		}
+		return HttpCookie.parse(cookieStr);
+	}
 	// ----------------------------------------------------------------------------------------- Private method start
 
 	/**

+ 47 - 0
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<TestBean> 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<String, TestBean> 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<TestBean> 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<String, Integer> 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<String> keys = CollUtil.newArrayList("a", "b", "c", "d");
+		final Map<String, String> 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<String> list = CollUtil.newArrayList("a", "b", "c", "c", "a", "b", "d");
+		Map<String, Integer> 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"));
+	}
 }

+ 3 - 14
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
@@ -14,17 +14,6 @@ import org.junit.Test;
 public class IterUtilTest {
 	
 	@Test
-	public void countMapTest() {
-		ArrayList<String> list = CollUtil.newArrayList("a", "b", "c", "c", "a", "b", "d");
-		Map<String, Integer> 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<Car> carList = CollUtil.newArrayList(new Car("123", "大众"), new Car("345", "奔驰"), new Car("567", "路虎"));
 		Map<String, Car> carNameMap = IterUtil.fieldValueMap(carList, "carNumber");

+ 16 - 0
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<HttpCookie> 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());
+	}
 }

+ 10 - 44
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<String, Cookie> readCookieMap(HttpServletRequest httpServletRequest) {
-		final Map<String, Cookie> 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请求相关<br>
-	 * 
-	 * @param checkString 被检测的字符串
-	 * @return 是否未知
-	 */
-	private static boolean isUnknow(String checkString) {
-		return StrUtil.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
-	}
-	// --------------------------------------------------------- Private methd end
-
 }

+ 129 - 3
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<String, HttpCookie> cookieCache;
+
 	/**
 	 * 构造
 	 *
@@ -103,6 +112,16 @@ public class HttpRequest {
 	 * @param headerKey 头信息的KEY
 	 * @return header值
 	 */
+	public String getHeader(Header headerKey) {
+		return getHeader(headerKey.toString());
+	}
+
+	/**
+	 * 获得请求header中的信息
+	 *
+	 * @param headerKey 头信息的KEY
+	 * @return header值
+	 */
 	public String getHeader(String headerKey) {
 		return getHeaders().getFirst(headerKey);
 	}
@@ -123,12 +142,21 @@ public class HttpRequest {
 	}
 
 	/**
+	 * 获取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);
 	}
 
 	/**
@@ -141,6 +169,49 @@ public class HttpRequest {
 	}
 
 	/**
+	 * 获得Cookie信息字符串
+	 *
+	 * @return cookie字符串
+	 */
+	public String getCookiesStr() {
+		return getHeader(Header.COOKIE);
+	}
+
+	/**
+	 * 获得Cookie信息列表
+	 *
+	 * @return Cookie信息列表
+	 */
+	public Collection<HttpCookie> getCookies() {
+		return getCookieMap().values();
+	}
+
+	/**
+	 * 获得Cookie信息Map,键为Cookie名,值为HttpCookie对象
+	 *
+	 * @return Cookie信息Map
+	 */
+	public Map<String, HttpCookie> 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);
+	}
+
+	/**
 	 * 获取请求体的流,流中可以读取请求内容,包括请求表单数据或文件上传数据
 	 *
 	 * @return 流
@@ -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
+	 *
+	 * <p>
+	 * 默认检测的Header:
+	 *
+	 * <pre>
+	 * 1、X-Forwarded-For
+	 * 2、X-Real-IP
+	 * 3、Proxy-Client-IP
+	 * 4、WL-Proxy-Client-IP
+	 * </pre>
+	 *
+	 * <p>
+	 * otherHeaderNames参数用于自定义检测的Header<br>
+	 * 需要注意的是,使用此方法获取的客户IP地址必须在Http服务器(例如Nginx)中配置头信息,否则容易造成IP伪造。
+	 * </p>
+	 *
+	 * @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
+	 *
+	 * <p>
+	 * headerNames参数用于自定义检测的Header<br>
+	 * 需要注意的是,使用此方法获取的客户IP地址必须在Http服务器(例如Nginx)中配置头信息,否则容易造成IP伪造。
+	 * </p>
+	 *
+	 * @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);
+	}
 }