diff --git a/hutool-core/src/main/java/cn/hutool/core/comparator/IndexedComparator.java b/hutool-core/src/main/java/cn/hutool/core/comparator/IndexedComparator.java index d54cd3c2f..71026b22e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/comparator/IndexedComparator.java +++ b/hutool-core/src/main/java/cn/hutool/core/comparator/IndexedComparator.java @@ -1,9 +1,10 @@ package cn.hutool.core.comparator; import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ArrayUtil; import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; /** * 按照数组的顺序正序排列,数组的元素位置决定了对象的排序先后
@@ -16,7 +17,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; /** * 构造 @@ -28,6 +32,19 @@ public class IndexedComparator implements Comparator { this(false, objs); } + + /** + * 构造 + * + * @param atEndIfMiss 如果不在列表中是否排在后边 + * @param map 参与排序的map,map中的value值大小决定了对象的排序先后 + */ + @SuppressWarnings("unchecked") + private IndexedComparator(boolean atEndIfMiss, Map map) { + this.atEndIfMiss = atEndIfMiss; + this.map = map; + } + /** * 构造 * @@ -38,7 +55,10 @@ public class IndexedComparator implements Comparator { public IndexedComparator(boolean atEndIfMiss, T... objs) { Assert.notNull(objs, "'objs' array must not be null"); this.atEndIfMiss = atEndIfMiss; - this.array = objs; + map = new HashMap<>(1 + (int) (objs.length / 0.75)); + for (int i = 0; i < objs.length; i++) { + map.put(objs[i], i); + } } @Override @@ -47,8 +67,8 @@ public class IndexedComparator implements Comparator { final int index2 = getOrder(o2); if (index1 == index2) { - if (index1 < 0 || index1 == this.array.length) { - // 任意一个元素不在列表中, 返回原顺序 + if (index1 < 0 || index1 == this.map.size()) { + // 任意一个元素不在map中, 返回原顺序 return 1; } @@ -60,15 +80,15 @@ public class IndexedComparator implements Comparator { } /** - * 查找对象类型所在列表的位置 + * 查找对象类型所对应的顺序值,即在原列表中的顺序 * * @param object 对象 - * @return 位置,未找到位置根据{@link #atEndIfMiss}取不同值,false返回-1,否则返回列表长度 + * @return 位置,未找到位置根据{@link #atEndIfMiss}取不同值,false返回-1,否则返回map长度 */ private int getOrder(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/cn/hutool/core/comparator/ArrayIndexedComparator.java b/hutool-core/src/test/java/cn/hutool/core/comparator/ArrayIndexedComparator.java new file mode 100644 index 000000000..44bd6c929 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/comparator/ArrayIndexedComparator.java @@ -0,0 +1,75 @@ +package cn.hutool.core.comparator; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.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(T... objs) { + this(false, objs); + } + + /** + * 构造 + * + * @param atEndIfMiss 如果不在列表中是否排在后边 + * @param objs 参与排序的数组,数组的元素位置决定了对象的排序先后 + */ + @SuppressWarnings("unchecked") + public ArrayIndexedComparator(boolean atEndIfMiss, T... objs) { + Assert.notNull(objs, "'objs' array must not be null"); + this.atEndIfMiss = atEndIfMiss; + this.array = objs; + } + + @Override + public int compare(T o1, 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 0; + } + + return Integer.compare(index1, index2); + } + + /** + * 查找对象类型所在列表的位置 + * + * @param object 对象 + * @return 位置,未找到位置根据{@link #atEndIfMiss}取不同值,false返回-1,否则返回列表长度 + */ + private int getOrder(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/test/java/cn/hutool/core/comparator/IndexedComparatorTest.java b/hutool-core/src/test/java/cn/hutool/core/comparator/IndexedComparatorTest.java new file mode 100644 index 000000000..f6bede779 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/comparator/IndexedComparatorTest.java @@ -0,0 +1,74 @@ +package cn.hutool.core.comparator; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.StopWatch; +import cn.hutool.core.lang.Console; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +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 = CollectionUtil.sort(set, new ArrayIndexedComparator<>(arr)); + + Assert.assertEquals("a", sortSet.get(0)); + Assert.assertEquals( new User("9", null), sortSet.get(2)); + Assert.assertEquals(3, sortSet.get(4)); + Assert.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 = CollectionUtil.sort(set, new ArrayIndexedComparator<>(arr).reversed()); + + Assert.assertEquals("a", sortSet.get(6)); + Assert.assertNull(sortSet.get(1)); + Assert.assertEquals( new User("9", null), sortSet.get(4)); + Assert.assertEquals(3, sortSet.get(2)); + } + + @Test + @Ignore + 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++) { + final List sortSet = CollectionUtil.sort(set, new IndexedComparator<>(arr)); + } + stopWatch.stop(); + + + stopWatch.start(); + for (int i = 0; i < 10_000_000; i++) { + final List sortSet = CollectionUtil.sort(set, new ArrayIndexedComparator<>(arr)); + } + stopWatch.stop(); + Console.log(stopWatch.prettyPrint()); + } + + @Data + @AllArgsConstructor + static class User{ + private String a; + private String b; + } +}