ソースを参照

fix bug and add method

Looly 5 年 前
コミット
efedd36696

+ 4 - 0
CHANGELOG.md

@@ -6,7 +6,11 @@
 ## 5.3.1 (2020-04-11)
 
 ### 新特性
+* 【core   】     ListUtil、MapUtil、CollUtil增加empty方法
+
 ### Bug修复
+* 【json   】     修复解析JSON字符串时配置无法传递问题
+* 【core   】     修复ServletUtil.readCookieMap空指针问题(issue#827@Github)
 
 -------------------------------------------------------------------------------------------------------------
 

+ 41 - 3
hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java

@@ -38,8 +38,10 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.NavigableSet;
 import java.util.Objects;
 import java.util.Set;
+import java.util.SortedSet;
 import java.util.Stack;
 import java.util.TreeMap;
 import java.util.TreeSet;
@@ -2362,15 +2364,14 @@ public class CollUtil {
 	}
 
 	/**
-	 * 循环遍历Map,使用{@link KVConsumer} 接受遍历的每条数据,并针对每条数据做处理
+	 * 循环遍历Map,使用{@link KVConsumer} 接受遍历的每条数据,并针对每条数据做处理<br>
+	 * 和JDK8中的map.forEach不同的是,此方法支持index
 	 *
 	 * @param <K>        Key类型
 	 * @param <V>        Value类型
 	 * @param map        {@link Map}
 	 * @param kvConsumer {@link KVConsumer} 遍历的每条数据处理器
-	 * @deprecated JDK8+中使用map.forEach
 	 */
-	@Deprecated
 	public static <K, V> void forEach(Map<K, V> map, KVConsumer<K, V> kvConsumer) {
 		int index = 0;
 		for (Entry<K, V> entry : map.entrySet()) {
@@ -2561,6 +2562,43 @@ public class CollUtil {
 		return Collections.unmodifiableCollection(c);
 	}
 
+	/**
+	 * 根据给定的集合类型,返回对应的空集合,支持类型包括:
+	 * *
+	 * <pre>
+	 *     1. NavigableSet
+	 *     2. SortedSet
+	 *     3. Set
+	 *     4. List
+	 * </pre>
+	 *
+	 * @param <E> 元素类型
+	 * @param <T> 集合类型
+	 * @return 空集合
+	 * @since 5.3.1
+	 */
+	@SuppressWarnings("unchecked")
+	public static <E, T extends Collection<E>> T empty(Class<?> collectionClass) {
+		if (null == collectionClass) {
+			return (T) Collections.emptyList();
+		}
+
+		if (Set.class.isAssignableFrom(collectionClass)) {
+			if (NavigableSet.class == collectionClass) {
+				return (T) Collections.emptyNavigableSet();
+			} else if (SortedSet.class == collectionClass) {
+				return (T) Collections.emptySortedSet();
+			} else {
+				return (T) Collections.emptySet();
+			}
+		} else if (List.class.isAssignableFrom(collectionClass)) {
+			return (T) Collections.emptyList();
+		}
+
+		// 不支持空集合的集合类型
+		throw new IllegalArgumentException(StrUtil.format("[{}] is not support to get empty!", collectionClass));
+	}
+
 	// ---------------------------------------------------------------------------------------------- Interface start
 
 	/**

+ 13 - 0
hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java

@@ -9,6 +9,7 @@ import cn.hutool.core.util.ReflectUtil;
 import cn.hutool.core.util.StrUtil;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -676,4 +677,16 @@ public class IterUtil {
 		}
 		return map;
 	}
+
+	/**
+	 * 返回一个空Iterator
+	 *
+	 * @param <T> 元素类型
+	 * @return 空Iterator
+	 * @see Collections#emptyIterator()
+	 * @since 5.3.1
+	 */
+	public static <T> Iterator<T> empty() {
+		return Collections.emptyIterator();
+	}
 }

+ 17 - 6
hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java

@@ -425,13 +425,13 @@ public class ListUtil {
 	/**
 	 * 获取匹配规则定义中匹配到元素的所有位置
 	 *
-	 * @param <T> 元素类型
-	 * @param list 列表
+	 * @param <T>     元素类型
+	 * @param list    列表
 	 * @param matcher 匹配器,为空则全部匹配
 	 * @return 位置数组
 	 * @since 5.2.5
 	 */
-	public static <T> int[] indexOfAll(List<T> list, Matcher<T> matcher){
+	public static <T> int[] indexOfAll(List<T> list, Matcher<T> matcher) {
 		final List<Integer> indexList = new ArrayList<>();
 		if (null != list) {
 			int index = 0;
@@ -448,12 +448,23 @@ public class ListUtil {
 	/**
 	 * 将对应List转换为不可修改的List
 	 *
-	 * @param list Map
-	 * @param <T> 元素类型
-	 * @return 不修改Map
+	 * @param list List
+	 * @param <T>  元素类型
+	 * @return 不可修改List
 	 * @since 5.2.6
 	 */
 	public static <T> List<T> unmodifiable(List<T> list) {
 		return Collections.unmodifiableList(list);
 	}
+
+	/**
+	 * 获取一个空List
+	 *
+	 * @param <T> 元素类型
+	 * @return 空的List
+	 * @since 5.2.6
+	 */
+	public static <T> List<T> empty() {
+		return Collections.emptyList();
+	}
 }

+ 49 - 1
hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java

@@ -22,7 +22,9 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.NavigableMap;
 import java.util.Set;
+import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -687,8 +689,8 @@ public class MapUtil {
 	 * @param <T> 键和值类型
 	 * @param map Map对象,键值类型必须一致
 	 * @return 互换后的Map
-	 * @since 3.2.2
 	 * @see #inverse(Map)
+	 * @since 3.2.2
 	 */
 	public static <T> Map<T, T> reverse(Map<T, T> map) {
 		return filter(map, (Editor<Entry<T, T>>) t -> new Entry<T, T>() {
@@ -1067,4 +1069,50 @@ public class MapUtil {
 
 		return map;
 	}
+
+	/**
+	 * 返回一个空Map
+	 *
+	 * @param <K> 键类型
+	 * @param <V> 值类型
+	 * @return 空Map
+	 * @see Collections#emptyMap()
+	 * @since 5.3.1
+	 */
+	public static <K, V> Map<K, V> empty() {
+		return Collections.emptyMap();
+	}
+
+	/**
+	 * 根据传入的Map类型不同,返回对应类型的空Map,支持类型包括:
+	 *
+	 * <pre>
+	 *     1. NavigableMap
+	 *     2. SortedMap
+	 *     3. Map
+	 * </pre>
+	 *
+	 * @param <K>      键类型
+	 * @param <V>      值类型
+	 * @param <T>      Map类型
+	 * @param mapClass Map类型,null返回默认的Map
+	 * @return 空Map
+	 * @since 5.3.1
+	 */
+	@SuppressWarnings("unchecked")
+	public static <K, V, T extends Map<K, V>> T empty(Class<?> mapClass) {
+		if (null == mapClass) {
+			return (T) Collections.emptyMap();
+		}
+		if (NavigableMap.class == mapClass) {
+			return (T) Collections.emptyNavigableMap();
+		} else if (SortedMap.class == mapClass) {
+			return (T) Collections.emptySortedMap();
+		} else if (Map.class == mapClass) {
+			return (T) Collections.emptyMap();
+		}
+
+		// 不支持空集合的集合类型
+		throw new IllegalArgumentException(StrUtil.format("[{}] is not support to get empty!", mapClass));
+	}
 }

+ 2 - 1
hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java

@@ -28,6 +28,7 @@ import java.nio.ByteBuffer;
 import java.nio.channels.SocketChannel;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -715,7 +716,7 @@ public class NetUtil {
 	 */
 	public static List<HttpCookie> parseCookies(String cookieStr){
 		if(StrUtil.isBlank(cookieStr)){
-			return CollUtil.newArrayList();
+			return Collections.emptyList();
 		}
 		return HttpCookie.parse(cookieStr);
 	}

+ 18 - 36
hutool-core/src/test/java/cn/hutool/core/collection/CollUtilTest.java

@@ -5,11 +5,14 @@ import cn.hutool.core.lang.Dict;
 import cn.hutool.core.lang.Editor;
 import cn.hutool.core.lang.Filter;
 import cn.hutool.core.map.MapUtil;
+import lombok.AllArgsConstructor;
+import lombok.Data;
 import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
@@ -20,6 +23,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.SortedSet;
 
 /**
  * 集合工具类单元测试
@@ -179,7 +183,6 @@ public class CollUtilTest {
 		map.put("c", "3");
 
 		final String[] result = new String[1];
-		//noinspection deprecation
 		CollUtil.forEach(map, (key, value, index) -> {
 			if (key.equals("a")) {
 				result[0] = value;
@@ -304,6 +307,20 @@ public class CollUtilTest {
 		Assert.assertEquals(new Integer(14), map.get("王五"));
 	}
 
+	@Test
+	public void emptyTest() {
+		final SortedSet<String> emptySortedSet = CollUtil.empty(SortedSet.class);
+		Assert.assertEquals(Collections.emptySortedSet(), emptySortedSet);
+
+		final Set<String> emptySet = CollUtil.empty(Set.class);
+		Assert.assertEquals(Collections.emptySet(), emptySet);
+
+		final List<String> emptyList = CollUtil.empty(List.class);
+		Assert.assertEquals(Collections.emptyList(), emptyList);
+	}
+
+	@Data
+	@AllArgsConstructor
 	public static class TestBean {
 		private String name;
 		private int age;
@@ -313,41 +330,6 @@ public class CollUtilTest {
 			this.name = name;
 			this.age = age;
 		}
-
-		public TestBean(String name, int age, Date createTime) {
-			this.name = name;
-			this.age = age;
-			this.createTime = createTime;
-		}
-
-		public String getName() {
-			return name;
-		}
-
-		public void setName(String name) {
-			this.name = name;
-		}
-
-		public int getAge() {
-			return age;
-		}
-
-		public void setAge(int age) {
-			this.age = age;
-		}
-
-		public Date getCreateTime() {
-			return createTime;
-		}
-
-		public void setCreateTime(Date createTime) {
-			this.createTime = createTime;
-		}
-
-		@Override
-		public String toString() {
-			return "TestBeans [name=" + name + ", age=" + age + "]";
-		}
 	}
 
 	@Test

+ 2 - 1
hutool-db/src/test/java/cn/hutool/db/CRUDTest.java

@@ -87,7 +87,8 @@ public class CRUDTest {
 
 	@Test
 	public void findInTest2() throws SQLException {
-		List<Entity> results = db.findAll(Entity.create("user").set("id", new Condition("id", new long[]{1, 2, 3})));
+		List<Entity> results = db.findAll(Entity.create("user")
+				.set("id", new Condition("id", new long[]{1, 2, 3})));
 		Assert.assertEquals(2, results.size());
 	}
 

+ 6 - 0
hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java

@@ -10,6 +10,7 @@ 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.map.MapUtil;
 import cn.hutool.core.net.NetUtil;
 import cn.hutool.core.net.multipart.MultipartFormData;
 import cn.hutool.core.net.multipart.UploadSetting;
@@ -418,6 +419,11 @@ public class ServletUtil {
 	 * @return Cookie map
 	 */
 	public static Map<String, Cookie> readCookieMap(HttpServletRequest httpServletRequest) {
+		final Cookie[] cookies = httpServletRequest.getCookies();
+		if(ArrayUtil.isEmpty(cookies)){
+			return MapUtil.empty();
+		}
+
 		return IterUtil.toMap(
 				new ArrayIter<>(httpServletRequest.getCookies()),
 				new CaseInsensitiveMap<>(),

+ 12 - 7
hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java

@@ -206,8 +206,13 @@ final class InternalJSONUtil {
 	}
 
 	/**
-	 * 默认情况下是否忽略null值的策略选择<br>
-	 * JavaBean默认忽略null值,其它对象不忽略
+	 * 默认情况下是否忽略null值的策略选择,以下对象不忽略null值,其它对象忽略:
+	 *
+	 * <pre>
+	 *     1. CharSequence
+	 *     2. JSONTokener
+	 *     3. Map
+	 * </pre>
 	 *
 	 * @param obj 需要检查的对象
 	 * @return 是否忽略null值
@@ -234,13 +239,13 @@ final class InternalJSONUtil {
 
 		//默认使用时间戳
 		long timeMillis;
-		if(dateObj instanceof TemporalAccessor){
-			timeMillis = DateUtil.toInstant((TemporalAccessor)dateObj).toEpochMilli();
-		} else if(dateObj instanceof  Date){
+		if (dateObj instanceof TemporalAccessor) {
+			timeMillis = DateUtil.toInstant((TemporalAccessor) dateObj).toEpochMilli();
+		} else if (dateObj instanceof Date) {
 			timeMillis = ((Date) dateObj).getTime();
-		} else if(dateObj instanceof Calendar){
+		} else if (dateObj instanceof Calendar) {
 			timeMillis = ((Calendar) dateObj).getTimeInMillis();
-		} else{
+		} else {
 			throw new UnsupportedOperationException("Unsupported Date type: " + dateObj.getClass());
 		}
 		return String.valueOf(timeMillis);

+ 1 - 1
hutool-json/src/main/java/cn/hutool/json/JSONArray.java

@@ -20,7 +20,7 @@ import java.util.List;
 import java.util.ListIterator;
 import java.util.RandomAccess;
 
-import static cn.hutool.json.JSONConverter.*;
+import static cn.hutool.json.JSONConverter.jsonConvert;
 
 /**
  * JSON数组<br>

+ 2 - 1
hutool-json/src/main/java/cn/hutool/json/JSONObject.java

@@ -124,7 +124,8 @@ public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object>
 	 * <li>value为Map,将键值对加入JSON对象</li>
 	 * <li>value为JSON字符串(CharSequence),使用JSONTokener解析</li>
 	 * <li>value为JSONTokener,直接解析</li>
-	 * <li>value为普通JavaBean,如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"</li>
+	 * <li>value为普通JavaBean,如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象。
+	 * 例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"</li>
 	 * </ol>
 	 * 
 	 * @param source JavaBean或者Map对象或者String

+ 2 - 1
hutool-json/src/main/java/cn/hutool/json/JSONTokener.java

@@ -45,7 +45,7 @@ public class JSONTokener {
 	/**
 	 * JSON配置
 	 */
-	private JSONConfig config;
+	private final JSONConfig config;
 
 	// ------------------------------------------------------------------------------------ Constructor start
 
@@ -63,6 +63,7 @@ public class JSONTokener {
 		this.index = 0;
 		this.character = 1;
 		this.line = 1;
+		this.config = config;
 	}
 
 	/**

+ 11 - 0
hutool-json/src/test/java/cn/hutool/json/JSONArrayTest.java

@@ -46,6 +46,17 @@ public class JSONArrayTest {
 	}
 
 	@Test
+	public void parseWithNullTest() {
+		String jsonStr = "[{\"grep\":\"4.8\",\"result\":\"右\"},{\"grep\":\"4.8\",\"result\":null}]";
+		JSONArray jsonArray = JSONUtil.parseArray(jsonStr);
+		Assert.assertFalse(jsonArray.getJSONObject(1).containsKey("result"));
+
+		// 不忽略null,则null的键值对被保留
+		jsonArray = new JSONArray(jsonStr, false);
+		Assert.assertTrue(jsonArray.getJSONObject(1).containsKey("result"));
+	}
+
+	@Test
 	public void parseFileTest() {
 		JSONArray array = JSONUtil.readJSONArray(FileUtil.file("exam_test.json"), CharsetUtil.CHARSET_UTF_8);
 

+ 3 - 3
hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java

@@ -186,11 +186,11 @@ public class JSONObjectTest {
 	}
 
 	@Test
-	public void toBeanTest3() {
+	public void toBeanWithNullTest() {
 		String jsonStr = "{'data':{'userName':'ak','password': null}}";
+		Console.log(JSONUtil.parseObj(jsonStr));
 		UserWithMap user = JSONUtil.toBean(JSONUtil.parseObj(jsonStr), UserWithMap.class);
-		// Bean默认忽略null
-		Assert.assertFalse(user.getData().containsKey("password"));
+		Assert.assertTrue(user.getData().containsKey("password"));
 	}
 
 	@Test