From e28ecbb993dd34a97666795ad5fbb1c96697aee7 Mon Sep 17 00:00:00 2001 From: duanluan Date: Fri, 27 Jan 2023 22:04:51 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E6=AF=8F=E4=B8=AA=E5=AF=B9=E8=B1=A1=E7=9A=84=E6=AF=8F=E4=B8=AA?= =?UTF-8?q?=E5=85=83=E7=B4=A0=E9=83=BD=E7=9B=B8=E7=AD=89=EF=BC=88isAllEqua?= =?UTF-8?q?ls=EF=BC=89=E6=96=B9=E6=B3=95=E9=94=99=E8=AF=AF=E5=B9=B6?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=A6=86=E7=9B=96=E7=8E=87=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/top/csaf/CollectionUtils.java | 219 +++++++++++------- .../top/csaf/junit/CollectionUtilsTest.java | 97 ++++++-- 2 files changed, 218 insertions(+), 98 deletions(-) diff --git a/src/main/java/top/csaf/CollectionUtils.java b/src/main/java/top/csaf/CollectionUtils.java index ff87b9d..6603d2b 100644 --- a/src/main/java/top/csaf/CollectionUtils.java +++ b/src/main/java/top/csaf/CollectionUtils.java @@ -345,15 +345,15 @@ public class CollectionUtils { } /** - * 转为字符串,Float、Double、BigDecimal 小数点后多余的 0 会被去除 + * 转为字符串,Number 小数点后多余的 0 会被去除 * * @param object 需转换的对象 - * @param isByString 是否转换 + * @param isToString 是否转换 * @return 字符串 */ - private static Object stripTrailingZerosToString(Object object, boolean isByString) { - if (isByString) { - if (object instanceof Float || object instanceof Double || object instanceof BigDecimal) { + private static Object stripTrailingZerosToString(Object object, final boolean isToString) { + if (isToString) { + if (object instanceof Number) { object = new BigDecimal(object.toString()).stripTrailingZeros().toPlainString(); } else { object = object.toString(); @@ -365,129 +365,160 @@ public class CollectionUtils { /** * 是否 每个对象的每个元素都相等 * - * @param isByString 是否根据 toString() 的值来判断是否相等,Float、Double、BigDecimal 小数点后多余的 0 会被去除 - * @param continueFunction 对象何时不参与判断 - * @param objects 多个对象 + * @param isToString 是否根据 toString() 的值来判断是否相等, + * Number 会去除小数点后多余的 0。 + * @param continueFunction 对象何时不参与判断。Iterator、Enumeration 会在判断前转换为 List。 + * @param objects 多个对象。Iterator、Enumeration 会被使用掉。 * @return 是否 每个对象的每个元素都相等 */ - public static boolean isAllEquals(boolean isByString, Function continueFunction, final Object... objects) { - if (objects == null) { - return true; - } - if (objects.length == 0) { - throw new IllegalArgumentException("Objects: length should be greater than 0"); + public static boolean isAllEquals(final boolean isToString, final Function continueFunction, @NonNull final Object... objects) { + if (objects.length < 2) { + throw new IllegalArgumentException("Objects: length must be greater than 1."); } + Object prevObj = null; - for (Object object : objects) { - // continueFunction 中可能已经使用了 Iterator,所以在调用前转换为 List - if (object instanceof Iterator) { - object = IteratorUtils.toList((Iterator) object); - } - // 对象指定条件时不参与判断 - if (continueFunction != null && continueFunction.apply(object)) { - continue; + boolean isAssigned = false; + for (int i = 0; i < objects.length; i++) { + Object object = objects[i]; + + // 满足条件时对象不参与判断 + if (continueFunction != null) { + // Iterator、Enumeration 在 continueFunction 可能需要循环,所以先转换为 List,避免后续无法循环 + if (object instanceof Iterator) { + object = IteratorUtils.toList((Iterator) object); + objects[i] = object; + } else if (object instanceof Enumeration) { + object = EnumerationUtils.toList((Enumeration) object); + objects[i] = object; + } + + if (Boolean.TRUE.equals(continueFunction.apply(object))) { + continue; + } } - if (object instanceof Collection || object instanceof Map) { + + if (object instanceof Iterable || object instanceof Iterator || object instanceof Map) { Iterator iterator; - if (object instanceof Collection) { - iterator = ((Collection) object).iterator(); - } else { + if (object instanceof Iterable) { + iterator = ((Iterable) object).iterator(); + } else if (object instanceof Map) { iterator = (((Map) object).values()).iterator(); + } else { + iterator = (Iterator) object; } - int i = 0; + while (iterator.hasNext()) { - // 基础类型转为 String - Object nextObj = stripTrailingZerosToString(iterator.next(), isByString); - // 首次判断前跳过第一次循环 - if (prevObj == null && i == 0) { - prevObj = nextObj; - i = 1; - continue; - } - if (!Objects.equals(prevObj, nextObj)) { - return false; + Object nextObj = iterator.next(); + /** {@link com.google.gson.JsonArray } 循环时需转换为 JsonPrimitive 后处理 */ + if (nextObj instanceof JsonPrimitive) { + JsonPrimitive jsonPrimitive = (JsonPrimitive) nextObj; + nextObj = jsonPrimitive.isNumber() ? jsonPrimitive.getAsBigDecimal() : jsonPrimitive.getAsString(); } - prevObj = nextObj; - } - } else if (object instanceof Iterable) { - int i = 0; - for (Object o : (Iterable) object) { - Object nextObj = stripTrailingZerosToString(o, isByString); - if (prevObj == null && i == 0) { + + // 转为字符串,Number 小数点后多余的 0 会被去除 + nextObj = stripTrailingZerosToString(nextObj, isToString); + // 首次判断前跳过第一次循环 + if (prevObj == null && !isAssigned) { + // 上一次元素赋值 prevObj = nextObj; - i = 1; + isAssigned = true; continue; } if (!Objects.equals(prevObj, nextObj)) { return false; } - prevObj = nextObj; } } else if (object instanceof Object[]) { - Object[] objects1 = (Object[]) object; - for (int i = 0; i < objects1.length; i++) { - Object nextObj = stripTrailingZerosToString(objects1[i], isByString); - if (prevObj == null && i == 0) { + Object[] array = (Object[]) object; + for (Object o : array) { + Object nextObj = stripTrailingZerosToString(o, isToString); + if (prevObj == null && !isAssigned) { prevObj = nextObj; - i = 1; + isAssigned = true; continue; } if (!Objects.equals(prevObj, nextObj)) { return false; } - prevObj = nextObj; } } else if (object instanceof Enumeration) { Enumeration enumeration = (Enumeration) object; - int i = 0; while (enumeration.hasMoreElements()) { - Object nextObj = stripTrailingZerosToString(enumeration.nextElement(), isByString); - if (prevObj == null && i == 0) { + Object nextObj = stripTrailingZerosToString(enumeration.nextElement(), isToString); + if (prevObj == null && !isAssigned) { prevObj = nextObj; - i = 1; + isAssigned = true; continue; } if (!Objects.equals(prevObj, nextObj)) { return false; } - prevObj = nextObj; } + } else { + return false; } } return true; } + /** + * 是否 每个对象的每个元素都相等 + *

+ * 会根据 toString() 的值来判断是否相等,Number 会去除小数点后多余的 0。 + * + * @param continueFunction 对象何时不参与判断。Iterator、Enumeration 会在判断前转换为 List。 + * @param objects 多个对象。Iterator、Enumeration 会被使用掉。 + * @return 是否 每个对象的每个元素都相等 + */ + public static boolean isAllEquals(final Function continueFunction, @NonNull final Object... objects) { + return isAllEquals(true, continueFunction, objects); + } + /** * 是否不满足 每个对象的每个元素都相等 * - * @param isByString 是否根据 toString() 的值来判断是否相等,Float、Double、BigDecimal 小数点后多余的 0 会被去除 - * @param continueFunction 对象何时不参与判断 - * @param objects 多个对象 + * @param isToString 是否根据 toString() 的值来判断是否相等, + * Number 会去除小数点后多余的 0。 + * @param continueFunction 对象何时不参与判断。Iterator、Enumeration 会在判断前转换为 List。 + * @param objects 多个对象。Iterator、Enumeration 会被使用掉。 * @return 是否不满足 每个对象的每个元素都相等 */ - public static boolean isNotAllEquals(boolean isByString, Function continueFunction, final Object... objects) { - return !isAllEquals(isByString, continueFunction, objects); + public static boolean isNotAllEquals(final boolean isToString, final Function continueFunction, final Object... objects) { + return !isAllEquals(isToString, continueFunction, objects); + } + + /** + * 是否不满足 每个对象的每个元素都相等 + *

+ * 会根据 toString() 的值来判断是否相等,Number 会去除小数点后多余的 0。 + * + * @param continueFunction 对象何时不参与判断。Iterator、Enumeration 会在判断前转换为 List。 + * @param objects 多个对象。Iterator、Enumeration 会被使用掉。 + * @return 是否不满足 每个对象的每个元素都相等 + */ + public static boolean isNotAllEquals(final Function continueFunction, @NonNull final Object... objects) { + return isNotAllEquals(true, continueFunction, objects); } /** * 是否 每个对象的同一位置的元素都相等 * - * @param isByString 是否根据 toString() 的值来判断是否相等, - * Float、Double、BigDecimal 会去除小数点后多余的 0。 - * @param continueFunction 对象何时不参与判断 + * @param isToString 是否根据 toString() 的值来判断是否相等, + * Number 会去除小数点后多余的 0。 + * @param continueFunction 对象何时不参与判断。Iterator、Enumeration 会在判断前转换为 List。 * @param objects 多个对象。Iterator、Enumeration 会被使用掉。 * @return 是否 每个对象的同一位置的元素都相等 */ - public static boolean isAllEqualsSameIndex(final boolean isByString, final Function continueFunction, @NonNull final Object... objects) { + public static boolean isAllEqualsSameIndex(final boolean isToString, final Function continueFunction, @NonNull final Object... objects) { if (objects.length < 2) { - throw new IllegalArgumentException("Objects: length must be greater than 1"); + throw new IllegalArgumentException("Objects: length must be greater than 1."); } Integer prevObjSize = null; for (int i = 0; i < objects.length; i++) { Object object = objects[i]; - // Iterator、Enumeration 获取对象元素长度时可能需要循环,所以先转换为 List,避免后续无法循环 + // Iterator、Enumeration 在 continueFunction 或获取对象元素长度时可能需要循环,所以先转换为 List,避免后续无法循环 if (object instanceof Iterator) { object = IteratorUtils.toList((Iterator) object); objects[i] = object; @@ -516,6 +547,8 @@ public class CollectionUtils { objSize = ((Map) object).values().size(); } else if (object instanceof Object[]) { objSize = ((Object[]) object).length; + } else { + return false; } // 当前对象元素长度和上一次循环的对象元素长度不一致时退出 if (prevObjSize != null && !prevObjSize.equals(objSize)) { @@ -532,7 +565,7 @@ public class CollectionUtils { } // Iterable、Map - if (object instanceof Iterable || object instanceof Iterator || object instanceof Map) { + if (object instanceof Iterable || object instanceof Map) { // 不同类型获取 Iterator Iterator iterator; if (object instanceof Iterable) { @@ -541,8 +574,7 @@ public class CollectionUtils { iterator = (((Map) object).values()).iterator(); } - int i = 0; - for (; iterator.hasNext(); i++) { + for (int i = 0; iterator.hasNext(); i++) { Object nextObj = iterator.next(); /** {@link com.google.gson.JsonArray } 循环时需转换为 JsonPrimitive 后处理 */ if (nextObj instanceof JsonPrimitive) { @@ -550,8 +582,8 @@ public class CollectionUtils { nextObj = jsonPrimitive.isNumber() ? jsonPrimitive.getAsBigDecimal() : jsonPrimitive.getAsString(); } - // 基础类型转为 String - nextObj = stripTrailingZerosToString(nextObj, isByString); + // 转为字符串,Number 小数点后多余的 0 会被去除 + nextObj = stripTrailingZerosToString(nextObj, isToString); // 如果上一次列表的长度 < i + 1,即 prevList(上一次列表)未填充完需要判断的第一个对象的所有元素 if (prevList.size() < i + 1) { prevList.add(nextObj); @@ -565,9 +597,9 @@ public class CollectionUtils { } // Object[] else if (object instanceof Object[]) { - Object[] objects1 = (Object[]) object; - for (int i = 0; i < objects1.length; i++) { - Object nextObj = stripTrailingZerosToString(objects1[i], isByString); + Object[] array = (Object[]) object; + for (int i = 0; i < array.length; i++) { + Object nextObj = stripTrailingZerosToString(array[i], isToString); if (prevList.size() < i + 1) { prevList.add(nextObj); continue; @@ -581,15 +613,42 @@ public class CollectionUtils { return true; } + /** + * 是否 每个对象的同一位置的元素都相等 + *

+ * 会根据 toString() 的值来判断是否相等,Number 会去除小数点后多余的 0。 + * + * @param continueFunction 对象何时不参与判断。Iterator、Enumeration 会在判断前转换为 List。 + * @param objects 多个对象。Iterator、Enumeration 会被使用掉。 + * @return 是否 每个对象的同一位置的元素都相等 + */ + public static boolean isAllEqualsSameIndex(final Function continueFunction, @NonNull final Object... objects) { + return isAllEqualsSameIndex(true, continueFunction, objects); + } + /** * 是否不满足 每个对象的同一位置的元素都相等 * - * @param isByString 是否根据 toString() 的值来判断是否相等,Float、Double、BigDecimal 小数点后多余的 0 会被去除 + * @param isToString 是否根据 toString() 的值来判断是否相等, + * Number 会去除小数点后多余的 0。 * @param continueFunction 对象何时不参与判断 - * @param objects 多个对象 + * @param objects 多个对象。Iterator、Enumeration 会被使用掉。 + * @return 是否不满足 每个对象的同一位置的元素都相等 + */ + public static boolean isNotAllEqualsSameIndex(final boolean isToString, final Function continueFunction, @NonNull final Object... objects) { + return !isAllEqualsSameIndex(isToString, continueFunction, objects); + } + + /** + * 是否不满足 每个对象的同一位置的元素都相等 + *

+ * 会根据 toString() 的值来判断是否相等,Number 会去除小数点后多余的 0。 + * + * @param continueFunction 对象何时不参与判断。Iterator、Enumeration 会在判断前转换为 List。 + * @param objects 多个对象。Iterator、Enumeration 会被使用掉。 * @return 是否不满足 每个对象的同一位置的元素都相等 */ - public static boolean isNotAllEqualsSameIndex(boolean isByString, Function continueFunction, final Object... objects) { - return !isAllEqualsSameIndex(isByString, continueFunction, objects); + public static boolean isNotAllEqualsSameIndex(final Function continueFunction, @NonNull final Object... objects) { + return isNotAllEqualsSameIndex(true, continueFunction, objects); } } diff --git a/src/test/java/top/csaf/junit/CollectionUtilsTest.java b/src/test/java/top/csaf/junit/CollectionUtilsTest.java index 39dbc7f..227a8b6 100644 --- a/src/test/java/top/csaf/junit/CollectionUtilsTest.java +++ b/src/test/java/top/csaf/junit/CollectionUtilsTest.java @@ -9,6 +9,7 @@ import top.csaf.CollectionUtils; import java.math.BigDecimal; import java.math.BigInteger; import java.util.*; +import java.util.function.Function; import static org.junit.jupiter.api.Assertions.*; @@ -80,29 +81,64 @@ public class CollectionUtilsTest { @DisplayName("是否 每个对象的每个元素都相等") @Test void isAllEquals() { - List list = new ArrayList<>(); + /** {@link CollectionUtils#isAllEquals(boolean, Function, Object...)} */ + assertThrows(NullPointerException.class, () -> CollectionUtils.isAllEquals(true, null, null)); + assertThrows(IllegalArgumentException.class, () -> CollectionUtils.isAllEquals(true, null, new Object[]{})); + + List list = new ArrayList<>(); list.add("1"); - List list1 = new ArrayList<>(); - list1.add("1"); - List list2 = new ArrayList<>(); + List list1 = new ArrayList<>(); + list1.add(new BigInteger("1")); + JsonArray jsonArray = new JsonArray(); + jsonArray.add('1'); Map map = new HashMap<>(); - map.put("", "1"); - println("忽略 null 和空元素:" + CollectionUtils.isAllEquals(false, CollectionUtils::sizeIsEmpty, list, list1.iterator(), null, list2, map)); - List list3 = new ArrayList<>(); - list3.add(null); - println("忽略 null 和空元素和元素为 null:" + CollectionUtils.isAllEquals(false, CollectionUtils::isAllEmpty, list, list1.iterator(), null, list2, list3, map)); - Vector vector = new Vector<>(); - vector.add(1); - List list4 = new ArrayList<>(); - list4.add(1.0f); - List list5 = new ArrayList<>(); - list5.add(new BigDecimal("1.0")); - println("忽略值类型、忽略 null 和空元素和元素为 null:" + CollectionUtils.isAllEquals(true, CollectionUtils::isAllEmpty, list, list1.iterator(), list2, list3, list4, list5, map, vector)); + map.put("1", new BigDecimal("1")); + Object[] array = new Object[]{1.0}; + Vector vector = new Vector<>(); + vector.add(1.0f); + assertTrue(CollectionUtils.isAllEquals(true, CollectionUtils::sizeIsEmpty, list, null, list1.iterator(), jsonArray, map, array, vector.elements())); + // continueFunction 为 null,后续判断到 Iterator。数组排在第一个用于填充上一个元素。 + assertTrue(CollectionUtils.isAllEquals(true, null, array, list1.iterator())); + // continueFunction 为 null,后续判断到 Enumeration。Enumeration 排在第一个用于填充上一个元素。 + assertTrue(CollectionUtils.isAllEquals(true, null, vector.elements(), list)); + // Iterable 元素不一致结束 + list.set(0, 2); + assertFalse(CollectionUtils.isAllEquals(true, null, list, list1)); + // 数组元素不一致结束 + array[0] = 2; + assertFalse(CollectionUtils.isAllEquals(true, null, list1, array)); + // Enumeration 元素不一致结束 + vector.set(0, 2); + assertFalse(CollectionUtils.isAllEquals(true, null, list1, vector.elements())); + // isToString = false + assertFalse(CollectionUtils.isAllEquals(false, null, list, null)); + + /** {@link CollectionUtils#isAllEquals(Function, Object...)} */ + assertThrows(NullPointerException.class, () -> CollectionUtils.isAllEquals(null, null)); + assertFalse(CollectionUtils.isAllEquals(null, new ArrayList<>(), null)); + } + + @DisplayName("是否不满足 每个对象的每个元素都相等") + @Test + void isNotAllEquals() { + /** {@link CollectionUtils#isNotAllEquals(boolean, Function, Object...)} */ + assertThrows(NullPointerException.class, () -> CollectionUtils.isNotAllEquals(true, null, null)); + + List list = new ArrayList<>(); + list.add(1); + List list1 = new ArrayList<>(); + list1.add(2); + assertTrue(CollectionUtils.isNotAllEquals(true, null, list, list1)); + + /** {@link CollectionUtils#isNotAllEquals(Function, Object...)} */ + assertThrows(NullPointerException.class, () -> CollectionUtils.isNotAllEquals(null, null)); + assertTrue(CollectionUtils.isNotAllEquals(null, new ArrayList<>(), null)); } @DisplayName("是否 每个对象的同一位置的元素都相等") @Test void isAllEqualsSameIndex() { + /** {@link CollectionUtils#isAllEquals(boolean, Function, Object...)} */ assertThrows(NullPointerException.class, () -> CollectionUtils.isAllEqualsSameIndex(true, null, null)); assertThrows(IllegalArgumentException.class, () -> CollectionUtils.isAllEqualsSameIndex(true, null, new ArrayList<>())); @@ -128,9 +164,9 @@ public class CollectionUtilsTest { vector.add(2.0); vector.add(3); assertTrue(CollectionUtils.isAllEqualsSameIndex(true, CollectionUtils::sizeIsEmpty, list, null, list1.iterator(), jsonArray, map, array, vector.elements())); - // 非可循环对象且长度不一致判断结束 + // 长度不一致判断结束 list.remove(2); - assertFalse(CollectionUtils.isAllEqualsSameIndex(true, null, "", list)); + assertFalse(CollectionUtils.isAllEqualsSameIndex(true, null, new ArrayList<>(), list)); // 列表元素不一致结束 list.add(4); assertFalse(CollectionUtils.isAllEqualsSameIndex(true, null, list, list1.iterator())); @@ -139,5 +175,30 @@ public class CollectionUtilsTest { // 数组元素不一致结束 array[2] = 4; assertFalse(CollectionUtils.isAllEqualsSameIndex(true, CollectionUtils::sizeIsEmpty, list1, array)); + + /** {@link CollectionUtils#isAllEquals(Function, Object...)} */ + assertThrows(NullPointerException.class, () -> CollectionUtils.isAllEqualsSameIndex(null, null)); + assertFalse(CollectionUtils.isAllEqualsSameIndex(null, new ArrayList<>(), null)); + } + + @DisplayName("是否不满足 每个对象的同一位置的元素都相等") + @Test + void isNotAllEqualsSameIndex() { + /** {@link CollectionUtils#isNotAllEqualsSameIndex(boolean, Function, Object...)} */ + assertThrows(NullPointerException.class, () -> CollectionUtils.isNotAllEqualsSameIndex(true, null, null)); + + List list = new ArrayList<>(); + list.add("1"); + list.add(2); + list.add(new BigDecimal("3.0")); + List list1 = new ArrayList<>(); + list1.add(new BigInteger("1")); + list1.add(2.0f); + list1.add(3L); + assertFalse(CollectionUtils.isNotAllEqualsSameIndex(true, null, list, list1)); + + /** {@link CollectionUtils#isNotAllEqualsSameIndex(Function, Object...)} */ + assertThrows(NullPointerException.class, () -> CollectionUtils.isNotAllEqualsSameIndex(null, null)); + assertTrue(CollectionUtils.isNotAllEqualsSameIndex(null, new ArrayList<>(), null)); } } -- GitLab