diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/comparator/ArrayIndexedComparator.java b/hutool-core/src/main/java/org/dromara/hutool/core/comparator/ArrayIndexedComparator.java new file mode 100644 index 000000000..c157cb2f2 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/comparator/ArrayIndexedComparator.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.comparator; + +import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.array.ArrayUtil; + +import java.util.Comparator; + +/** + * 按照数组的顺序正序排列,数组的元素位置决定了对象的排序先后
+ * 默认的,如果参与排序的元素并不在数组中,则排序在前(可以通过atEndIfMiss设置) + * + * @param 被排序元素类型 + * @author looly + * @since 4.1.5 + */ +public class ArrayIndexedComparator implements Comparator { + + private final boolean atEndIfMiss; + private final T[] array; + + /** + * 构造 + * + * @param objs 参与排序的数组,数组的元素位置决定了对象的排序先后 + */ + @SuppressWarnings("unchecked") + public ArrayIndexedComparator(final T... objs) { + this(false, objs); + } + + /** + * 构造 + * + * @param atEndIfMiss 如果不在列表中是否排在后边 + * @param objs 参与排序的数组,数组的元素位置决定了对象的排序先后 + */ + @SuppressWarnings("unchecked") + public ArrayIndexedComparator(final boolean atEndIfMiss, final T... objs) { + Assert.notNull(objs, "'objs' array must not be null"); + this.atEndIfMiss = atEndIfMiss; + this.array = objs; + } + + @Override + public int compare(final T o1, final T o2) { + final int index1 = getOrder(o1); + final int index2 = getOrder(o2); + + // 任意一个元素不在列表中 + if(index1 == index2){ + if(index1 < 0 || index1 == this.array.length){ + // 任意一个元素不在列表中, 返回原顺序 + return 1; + } + } + + return Integer.compare(index1, index2); + } + + /** + * 查找对象类型所在列表的位置 + * + * @param object 对象 + * @return 位置,未找到位置根据{@link #atEndIfMiss}取不同值,false返回-1,否则返回列表长度 + */ + private int getOrder(final T object) { + int order = ArrayUtil.indexOf(array, object); + if (order < 0) { + order = this.atEndIfMiss ? this.array.length : -1; + } + return order; + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/comparator/CompareUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/comparator/CompareUtil.java index f23dec093..2dbf2e38a 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/comparator/CompareUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/comparator/CompareUtil.java @@ -335,7 +335,7 @@ public class CompareUtil { @SuppressWarnings("unchecked") public static Comparator comparingIndexed(final Function keyExtractor, final boolean atEndIfMiss, final U... objs) { Objects.requireNonNull(keyExtractor); - final IndexedComparator indexedComparator = new IndexedComparator<>(atEndIfMiss, objs); + final ArrayIndexedComparator indexedComparator = new ArrayIndexedComparator<>(atEndIfMiss, objs); return (o1, o2) -> indexedComparator.compare(keyExtractor.apply(o1), keyExtractor.apply(o2)); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/comparator/IndexedComparator.java b/hutool-core/src/main/java/org/dromara/hutool/core/comparator/IndexedComparator.java index bd732d9ff..fb2111b50 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/comparator/IndexedComparator.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/comparator/IndexedComparator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 looly(loolly@aliyun.com) + * Copyright (c) 2024. looly(loolly@aliyun.com) * Hutool is licensed under Mulan PSL v2. * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: @@ -13,9 +13,10 @@ package org.dromara.hutool.core.comparator; import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.core.array.ArrayUtil; import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; /** * 按照数组的顺序正序排列,数组的元素位置决定了对象的排序先后
@@ -28,7 +29,10 @@ import java.util.Comparator; public class IndexedComparator implements Comparator { private final boolean atEndIfMiss; - private final T[] array; + /** + * map存储对象类型所在列表的位置,k为对象,v为位置 + */ + private final Map map; /** * 构造 @@ -40,6 +44,18 @@ public class IndexedComparator implements Comparator { this(false, objs); } + + /** + * 构造 + * + * @param atEndIfMiss 如果不在列表中是否排在后边 + * @param map 参与排序的map,map中的value值大小决定了对象的排序先后 + */ + public IndexedComparator(final boolean atEndIfMiss, final Map map) { + this.atEndIfMiss = atEndIfMiss; + this.map = map; + } + /** * 构造 * @@ -50,7 +66,10 @@ public class IndexedComparator implements Comparator { public IndexedComparator(final boolean atEndIfMiss, final T... objs) { Assert.notNull(objs, "'objs' array must not be null"); this.atEndIfMiss = atEndIfMiss; - this.array = objs; + map = new HashMap<>(objs.length, 1); + for (int i = 0; i < objs.length; i++) { + map.put(objs[i], i); + } } @Override @@ -58,27 +77,29 @@ public class IndexedComparator implements Comparator { final int index1 = getOrder(o1); final int index2 = getOrder(o2); - // 任意一个元素不在列表中 - if(index1 == index2){ - if(index1 < 0 || index1 == this.array.length){ - // 任意一个元素不在列表中, 返回原顺序 + if (index1 == index2) { + if (index1 < 0 || index1 == this.map.size()) { + // 任意一个元素不在map中, 返回原顺序 return 1; } + + // 位置一样,认为是同一个元素 + return 0; } return Integer.compare(index1, index2); } /** - * 查找对象类型所在列表的位置 + * 查找对象类型所对应的顺序值,即在原列表中的顺序 * * @param object 对象 - * @return 位置,未找到位置根据{@link #atEndIfMiss}取不同值,false返回-1,否则返回列表长度 + * @return 位置,未找到位置根据{@link #atEndIfMiss}取不同值,false返回-1,否则返回map长度 */ private int getOrder(final T object) { - int order = ArrayUtil.indexOf(array, object); - if (order < 0) { - order = this.atEndIfMiss ? this.array.length : -1; + Integer order = map.get(object); + if (order == null) { + order = this.atEndIfMiss ? this.map.size() : -1; } return order; } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/comparator/IndexedComparatorTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/comparator/IndexedComparatorTest.java new file mode 100644 index 000000000..b1ecdbc63 --- /dev/null +++ b/hutool-core/src/test/java/org/dromara/hutool/core/comparator/IndexedComparatorTest.java @@ -0,0 +1,78 @@ +package org.dromara.hutool.core.comparator; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.dromara.hutool.core.collection.CollUtil; +import org.dromara.hutool.core.date.StopWatch; +import org.dromara.hutool.core.lang.Console; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * https://gitee.com/dromara/hutool/pulls/1240 + */ +public class IndexedComparatorTest { + @Test + public void sortTest() { + final Object[] arr = {"a", "b", new User("9", null), "1", 3, null, "2"}; + final Collection set = new HashSet<>(Arrays.asList(arr)); + + + final List sortSet = CollUtil.sort(set, new ArrayIndexedComparator<>(arr)); + + assertEquals("a", sortSet.get(0)); + assertEquals(new User("9", null), sortSet.get(2)); + assertEquals(3, sortSet.get(4)); + assertNull(sortSet.get(5)); + } + + @Test + public void reversedTest() { + final Object[] arr = {"a", "b", new User("9", null), "1", 3, null, "2"}; + final Collection set = new HashSet<>(Arrays.asList(arr)); + + final List sortSet = CollUtil.sort(set, new ArrayIndexedComparator<>(arr).reversed()); + + assertEquals("a", sortSet.get(6)); + assertNull(sortSet.get(1)); + assertEquals(new User("9", null), sortSet.get(4)); + assertEquals(3, sortSet.get(2)); + } + + @Test + @Disabled + public void benchmarkSortTest() { + final Object[] arr = {"a", "b", new User("9", null), "1", 3, null, "2"}; + final Collection set = new HashSet<>(Arrays.asList(arr)); + + final StopWatch stopWatch = new StopWatch(); + + stopWatch.start(); + for (int i = 0; i < 10_000_000; i++) { + CollUtil.sort(set, new IndexedComparator<>(arr)); + } + stopWatch.stop(); + + + stopWatch.start(); + for (int i = 0; i < 10_000_000; i++) { + CollUtil.sort(set, new ArrayIndexedComparator<>(arr)); + } + stopWatch.stop(); + Console.log(stopWatch.prettyPrint()); + } + + @Data + @AllArgsConstructor + static class User { + private String a; + private String b; + } +} diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelWriter.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelWriter.java index c126d1a07..536e94a79 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelWriter.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelWriter.java @@ -22,7 +22,7 @@ import org.apache.poi.xssf.usermodel.XSSFDataValidation; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.collection.ListUtil; -import org.dromara.hutool.core.comparator.IndexedComparator; +import org.dromara.hutool.core.comparator.ArrayIndexedComparator; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.file.FileUtil; @@ -1440,7 +1440,7 @@ public class ExcelWriter extends ExcelBase { Comparator aliasComparator = this.aliasComparator; if (null == aliasComparator) { final Set keySet = this.headerAlias.keySet(); - aliasComparator = new IndexedComparator<>(keySet.toArray(new String[0])); + aliasComparator = new ArrayIndexedComparator<>(keySet.toArray(new String[0])); this.aliasComparator = aliasComparator; } return aliasComparator;