提交 e28ecbb9 编写于 作者: 勤为径苦作舟's avatar 勤为径苦作舟

fix: 修复 是否每个对象的每个元素都相等(isAllEquals)方法错误并完善覆盖率测试

上级 c19b53ce
......@@ -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<Object, Boolean> 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<Object, Boolean> 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;
}
/**
* 是否 每个对象的每个元素都相等
* <p>
* 会根据 toString() 的值来判断是否相等,Number 会去除小数点后多余的 0。
*
* @param continueFunction 对象何时不参与判断。Iterator、Enumeration 会在判断前转换为 List。
* @param objects 多个对象。Iterator、Enumeration 会被使用掉。
* @return 是否 每个对象的每个元素都相等
*/
public static boolean isAllEquals(final Function<Object, Boolean> 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<Object, Boolean> continueFunction, final Object... objects) {
return !isAllEquals(isByString, continueFunction, objects);
public static boolean isNotAllEquals(final boolean isToString, final Function<Object, Boolean> continueFunction, final Object... objects) {
return !isAllEquals(isToString, continueFunction, objects);
}
/**
* 是否不满足 每个对象的每个元素都相等
* <p>
* 会根据 toString() 的值来判断是否相等,Number 会去除小数点后多余的 0。
*
* @param continueFunction 对象何时不参与判断。Iterator、Enumeration 会在判断前转换为 List。
* @param objects 多个对象。Iterator、Enumeration 会被使用掉。
* @return 是否不满足 每个对象的每个元素都相等
*/
public static boolean isNotAllEquals(final Function<Object, Boolean> 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<Object, Boolean> continueFunction, @NonNull final Object... objects) {
public static boolean isAllEqualsSameIndex(final boolean isToString, final Function<Object, Boolean> 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;
}
/**
* 是否 每个对象的同一位置的元素都相等
* <p>
* 会根据 toString() 的值来判断是否相等,Number 会去除小数点后多余的 0。
*
* @param continueFunction 对象何时不参与判断。Iterator、Enumeration 会在判断前转换为 List。
* @param objects 多个对象。Iterator、Enumeration 会被使用掉。
* @return 是否 每个对象的同一位置的元素都相等
*/
public static boolean isAllEqualsSameIndex(final Function<Object, Boolean> 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<Object, Boolean> continueFunction, @NonNull final Object... objects) {
return !isAllEqualsSameIndex(isToString, continueFunction, objects);
}
/**
* 是否不满足 每个对象的同一位置的元素都相等
* <p>
* 会根据 toString() 的值来判断是否相等,Number 会去除小数点后多余的 0。
*
* @param continueFunction 对象何时不参与判断。Iterator、Enumeration 会在判断前转换为 List。
* @param objects 多个对象。Iterator、Enumeration 会被使用掉。
* @return 是否不满足 每个对象的同一位置的元素都相等
*/
public static boolean isNotAllEqualsSameIndex(boolean isByString, Function<Object, Boolean> continueFunction, final Object... objects) {
return !isAllEqualsSameIndex(isByString, continueFunction, objects);
public static boolean isNotAllEqualsSameIndex(final Function<Object, Boolean> continueFunction, @NonNull final Object... objects) {
return isNotAllEqualsSameIndex(true, continueFunction, objects);
}
}
......@@ -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<String> 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<Object> list = new ArrayList<>();
list.add("1");
List<String> list1 = new ArrayList<>();
list1.add("1");
List<String> list2 = new ArrayList<>();
List<Object> list1 = new ArrayList<>();
list1.add(new BigInteger("1"));
JsonArray jsonArray = new JsonArray();
jsonArray.add('1');
Map<String, Object> map = new HashMap<>();
map.put("", "1");
println("忽略 null 和空元素:" + CollectionUtils.isAllEquals(false, CollectionUtils::sizeIsEmpty, list, list1.iterator(), null, list2, map));
List<String> list3 = new ArrayList<>();
list3.add(null);
println("忽略 null 和空元素和元素为 null:" + CollectionUtils.isAllEquals(false, CollectionUtils::isAllEmpty, list, list1.iterator(), null, list2, list3, map));
Vector<Integer> vector = new Vector<>();
vector.add(1);
List<Float> list4 = new ArrayList<>();
list4.add(1.0f);
List<BigDecimal> 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<Object> 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<Object> list = new ArrayList<>();
list.add(1);
List<Object> 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<Object> list = new ArrayList<>();
list.add("1");
list.add(2);
list.add(new BigDecimal("3.0"));
List<Object> 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));
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册