mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-30 12:47:58 +08:00
优化IndexedComparator性能
This commit is contained in:
parent
3dd473b2c4
commit
fcd0d004e3
@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按照数组的顺序正序排列,数组的元素位置决定了对象的排序先后<br>
|
||||||
|
* 默认的,如果参与排序的元素并不在数组中,则排序在前(可以通过atEndIfMiss设置)
|
||||||
|
*
|
||||||
|
* @param <T> 被排序元素类型
|
||||||
|
* @author looly
|
||||||
|
* @since 4.1.5
|
||||||
|
*/
|
||||||
|
public class ArrayIndexedComparator<T> implements Comparator<T> {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -335,7 +335,7 @@ public class CompareUtil {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T, U> Comparator<T> comparingIndexed(final Function<? super T, ? extends U> keyExtractor, final boolean atEndIfMiss, final U... objs) {
|
public static <T, U> Comparator<T> comparingIndexed(final Function<? super T, ? extends U> keyExtractor, final boolean atEndIfMiss, final U... objs) {
|
||||||
Objects.requireNonNull(keyExtractor);
|
Objects.requireNonNull(keyExtractor);
|
||||||
final IndexedComparator<U> indexedComparator = new IndexedComparator<>(atEndIfMiss, objs);
|
final ArrayIndexedComparator<U> indexedComparator = new ArrayIndexedComparator<>(atEndIfMiss, objs);
|
||||||
return (o1, o2) -> indexedComparator.compare(keyExtractor.apply(o1), keyExtractor.apply(o2));
|
return (o1, o2) -> indexedComparator.compare(keyExtractor.apply(o1), keyExtractor.apply(o2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
* You can use this software according to the terms and conditions of the 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:
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
@ -13,9 +13,10 @@
|
|||||||
package org.dromara.hutool.core.comparator;
|
package org.dromara.hutool.core.comparator;
|
||||||
|
|
||||||
import org.dromara.hutool.core.lang.Assert;
|
import org.dromara.hutool.core.lang.Assert;
|
||||||
import org.dromara.hutool.core.array.ArrayUtil;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按照数组的顺序正序排列,数组的元素位置决定了对象的排序先后<br>
|
* 按照数组的顺序正序排列,数组的元素位置决定了对象的排序先后<br>
|
||||||
@ -28,7 +29,10 @@ import java.util.Comparator;
|
|||||||
public class IndexedComparator<T> implements Comparator<T> {
|
public class IndexedComparator<T> implements Comparator<T> {
|
||||||
|
|
||||||
private final boolean atEndIfMiss;
|
private final boolean atEndIfMiss;
|
||||||
private final T[] array;
|
/**
|
||||||
|
* map存储对象类型所在列表的位置,k为对象,v为位置
|
||||||
|
*/
|
||||||
|
private final Map<? super T, Integer> map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
@ -40,6 +44,18 @@ public class IndexedComparator<T> implements Comparator<T> {
|
|||||||
this(false, objs);
|
this(false, objs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param atEndIfMiss 如果不在列表中是否排在后边
|
||||||
|
* @param map 参与排序的map,map中的value值大小决定了对象的排序先后
|
||||||
|
*/
|
||||||
|
public IndexedComparator(final boolean atEndIfMiss, final Map<? super T, Integer> map) {
|
||||||
|
this.atEndIfMiss = atEndIfMiss;
|
||||||
|
this.map = map;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
@ -50,7 +66,10 @@ public class IndexedComparator<T> implements Comparator<T> {
|
|||||||
public IndexedComparator(final boolean atEndIfMiss, final T... objs) {
|
public IndexedComparator(final boolean atEndIfMiss, final T... objs) {
|
||||||
Assert.notNull(objs, "'objs' array must not be null");
|
Assert.notNull(objs, "'objs' array must not be null");
|
||||||
this.atEndIfMiss = atEndIfMiss;
|
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
|
@Override
|
||||||
@ -58,27 +77,29 @@ public class IndexedComparator<T> implements Comparator<T> {
|
|||||||
final int index1 = getOrder(o1);
|
final int index1 = getOrder(o1);
|
||||||
final int index2 = getOrder(o2);
|
final int index2 = getOrder(o2);
|
||||||
|
|
||||||
// 任意一个元素不在列表中
|
|
||||||
if (index1 == index2) {
|
if (index1 == index2) {
|
||||||
if(index1 < 0 || index1 == this.array.length){
|
if (index1 < 0 || index1 == this.map.size()) {
|
||||||
// 任意一个元素不在列表中, 返回原顺序
|
// 任意一个元素不在map中, 返回原顺序
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 位置一样,认为是同一个元素
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Integer.compare(index1, index2);
|
return Integer.compare(index1, index2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找对象类型所在列表的位置
|
* 查找对象类型所对应的顺序值,即在原列表中的顺序
|
||||||
*
|
*
|
||||||
* @param object 对象
|
* @param object 对象
|
||||||
* @return 位置,未找到位置根据{@link #atEndIfMiss}取不同值,false返回-1,否则返回列表长度
|
* @return 位置,未找到位置根据{@link #atEndIfMiss}取不同值,false返回-1,否则返回map长度
|
||||||
*/
|
*/
|
||||||
private int getOrder(final T object) {
|
private int getOrder(final T object) {
|
||||||
int order = ArrayUtil.indexOf(array, object);
|
Integer order = map.get(object);
|
||||||
if (order < 0) {
|
if (order == null) {
|
||||||
order = this.atEndIfMiss ? this.array.length : -1;
|
order = this.atEndIfMiss ? this.map.size() : -1;
|
||||||
}
|
}
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
@ -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<Object> set = new HashSet<>(Arrays.asList(arr));
|
||||||
|
|
||||||
|
|
||||||
|
final List<Object> 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<Object> set = new HashSet<>(Arrays.asList(arr));
|
||||||
|
|
||||||
|
final List<Object> 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<Object> 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;
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@ import org.apache.poi.xssf.usermodel.XSSFDataValidation;
|
|||||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||||
import org.dromara.hutool.core.bean.BeanUtil;
|
import org.dromara.hutool.core.bean.BeanUtil;
|
||||||
import org.dromara.hutool.core.collection.ListUtil;
|
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.IORuntimeException;
|
||||||
import org.dromara.hutool.core.io.IoUtil;
|
import org.dromara.hutool.core.io.IoUtil;
|
||||||
import org.dromara.hutool.core.io.file.FileUtil;
|
import org.dromara.hutool.core.io.file.FileUtil;
|
||||||
@ -1440,7 +1440,7 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
|
|||||||
Comparator<String> aliasComparator = this.aliasComparator;
|
Comparator<String> aliasComparator = this.aliasComparator;
|
||||||
if (null == aliasComparator) {
|
if (null == aliasComparator) {
|
||||||
final Set<String> keySet = this.headerAlias.keySet();
|
final Set<String> keySet = this.headerAlias.keySet();
|
||||||
aliasComparator = new IndexedComparator<>(keySet.toArray(new String[0]));
|
aliasComparator = new ArrayIndexedComparator<>(keySet.toArray(new String[0]));
|
||||||
this.aliasComparator = aliasComparator;
|
this.aliasComparator = aliasComparator;
|
||||||
}
|
}
|
||||||
return aliasComparator;
|
return aliasComparator;
|
||||||
|
Loading…
Reference in New Issue
Block a user