diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/comparator/WindowsExplorerStringComparator.java b/hutool-core/src/main/java/org/dromara/hutool/core/comparator/WindowsExplorerStringComparator.java
new file mode 100644
index 000000000..2bac97696
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/comparator/WindowsExplorerStringComparator.java
@@ -0,0 +1,99 @@
+/*
+ * 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:
+ * 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.text.StrUtil;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Windows 资源管理器风格字符串比较器
+ *
+ *
此比较器模拟了 Windows 资源管理器的文件名排序方式,可得到与其相同的排序结果。
+ *
+ * 假设有一个数组,包含若干个文件名 {@code {"abc2.doc", "abc1.doc", "abc12.doc"}}
+ * 在 Windows 资源管理器中以名称排序时,得到 {@code {"abc1.doc", "abc2.doc", "abc12.doc" }}
+ * 调用 {@code Arrays.sort(filenames);} 时,得到 {@code {"abc1.doc", "abc12.doc", "abc2.doc" }}
+ * 调用 {@code Arrays.sort(filenames, new WindowsExplorerStringComparator());} 时,得到 {@code {"abc1.doc", "abc2.doc",
+ * "abc12.doc" }},这与在资源管理器中看到的相同
+ *
+ * @author YMNNs
+ * @see
+ * Java - Sort Strings like Windows Explorer
+ */
+public class WindowsExplorerStringComparator implements Comparator {
+
+ /**
+ * 单例
+ */
+ public static final WindowsExplorerStringComparator INSTANCE = new WindowsExplorerStringComparator();
+
+ private static final Pattern splitPattern = Pattern.compile("\\d+|\\.|\\s");
+
+ @Override
+ public int compare(final CharSequence str1, final CharSequence str2) {
+ final Iterator i1 = splitStringPreserveDelimiter(str1).iterator();
+ final Iterator i2 = splitStringPreserveDelimiter(str2).iterator();
+ while (true) {
+ //Til here all is equal.
+ if (!i1.hasNext() && !i2.hasNext()) {
+ return 0;
+ }
+ //first has no more parts -> comes first
+ if (!i1.hasNext()) {
+ return -1;
+ }
+ //first has more parts than i2 -> comes after
+ if (!i2.hasNext()) {
+ return 1;
+ }
+
+ final String data1 = i1.next();
+ final String data2 = i2.next();
+ int result;
+ try {
+ //If both data are numbers, then compare numbers
+ result = Long.compare(Long.parseLong(data1), Long.parseLong(data2));
+ //If numbers are equal than longer comes first
+ if (result == 0) {
+ result = -Integer.compare(data1.length(), data2.length());
+ }
+ } catch (final NumberFormatException ex) {
+ //compare text case insensitive
+ result = data1.compareToIgnoreCase(data2);
+ }
+
+ if (result != 0) {
+ return result;
+ }
+ }
+ }
+
+ private List splitStringPreserveDelimiter(final CharSequence str) {
+ final Matcher matcher = splitPattern.matcher(str);
+ final List list = new ArrayList<>();
+ int pos = 0;
+ while (matcher.find()) {
+ list.add(StrUtil.sub(str, pos, matcher.start()));
+ list.add(matcher.group());
+ pos = matcher.end();
+ }
+ list.add(StrUtil.subSuf(str, pos));
+ return list;
+ }
+}
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/comparator/WindowsExplorerStringComparatorTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/comparator/WindowsExplorerStringComparatorTest.java
new file mode 100644
index 000000000..fe65fc347
--- /dev/null
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/comparator/WindowsExplorerStringComparatorTest.java
@@ -0,0 +1,67 @@
+package org.dromara.hutool.core.comparator;
+
+import org.dromara.hutool.core.collection.ListUtil;
+import org.dromara.hutool.core.lang.Assert;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class WindowsExplorerStringComparatorTest {
+
+ final List answer1 = ListUtil.of(
+ "filename",
+ "filename 00",
+ "filename 0",
+ "filename 01",
+ "filename.jpg",
+ "filename.txt",
+ "filename00.jpg",
+ "filename00a.jpg",
+ "filename00a.txt",
+ "filename0",
+ "filename0.jpg",
+ "filename0a.txt",
+ "filename0b.jpg",
+ "filename0b1.jpg",
+ "filename0b02.jpg",
+ "filename0c.jpg",
+ "filename01.0hjh45-test.txt",
+ "filename01.0hjh46",
+ "filename01.1hjh45.txt",
+ "filename01.hjh45.txt",
+ "Filename01.jpg",
+ "Filename1.jpg",
+ "filename2.hjh45.txt",
+ "filename2.jpg",
+ "filename03.jpg",
+ "filename3.jpg"
+ );
+
+ List answer2 = ListUtil.of(
+ "abc1.doc",
+ "abc2.doc",
+ "abc12.doc"
+ );
+
+ @Test
+ public void testCompare1() {
+ final List toSort = new ArrayList<>(answer1);
+ while (toSort.equals(answer1)) {
+ Collections.shuffle(toSort);
+ }
+ toSort.sort(WindowsExplorerStringComparator.INSTANCE);
+ Assert.equals(toSort, answer1);
+ }
+
+ @Test
+ public void testCompare2() {
+ final List toSort = new ArrayList<>(answer2);
+ while (toSort.equals(answer2)) {
+ Collections.shuffle(toSort);
+ }
+ toSort.sort(WindowsExplorerStringComparator.INSTANCE);
+ Assert.equals(toSort, answer2);
+ }
+}