引言
在Java开发中,按字母顺序排序列表是常见需求。本文将系统探讨多种排序方法,从基础的Collections
类到高级的RuleBasedCollator
,覆盖各种实际场景:
- 基础排序:自然顺序与逆序
- 自定义排序:处理大小写混合、特殊字符
- 现代API:Stream排序与TreeSet应用
- 国际化处理:本地化排序与重音字符处理
- 高级技巧:基于规则的排序器
每种方法都配有完整代码示例,助你快速掌握实战技巧。
使用Collections类排序
Collections
类提供了静态sort()
方法,支持自然排序和自定义比较器两种模式。
自然顺序/字典序排序
首先定义测试数据:
private static List<String> INPUT_NAMES = Arrays.asList("john", "mike", "usmon", "ken", "harry");
使用Collections.sort()
进行字典序排序:
@Test
void givenListOfStrings_whenUsingCollections_thenListIsSorted() {
Collections.sort(INPUT_NAMES);
assertThat(INPUT_NAMES).isEqualTo(EXPECTED_NATURAL_ORDER);
}
预期结果:
private static List<String> EXPECTED_NATURAL_ORDER = Arrays.asList("harry", "john", "ken", "mike", "usmon");
关键点:
- 列表按元素自然升序排列
- 要求元素实现
Comparable
接口(String
已实现) - 原地修改列表,无需返回新列表
逆序排序
通过自定义比较器实现逆序排序:
Comparator<String> reverseComparator = (first, second) -> second.compareTo(first);
或使用内置方法:
Comparator<String> reverseComparator = Comparator.reverseOrder();
完整测试用例:
@Test
void givenListOfStrings_whenUsingCollections_thenListIsSortedInReverse() {
Comparator<String> reverseComparator = Comparator.reverseOrder();
Collections.sort(INPUT_NAMES, reverseComparator);
assertThat(INPUT_NAMES).isEqualTo(EXPECTED_REVERSE_ORDER);
}
预期结果:
private static List<String> EXPECTED_REVERSE_ORDER = Arrays.asList("usmon", "mike", "ken", "john", "harry");
注意:由于String
是final类,无法通过继承重写compareTo
,必须使用Comparator
实现逆序。
使用Comparator实现自定义排序
当需要特殊排序逻辑时,Comparator
接口是最佳选择。
处理大小写混合列表
测试用例展示大小写敏感问题:
@Test
void givenListOfStringsWithUpperAndLowerCaseMixed_whenCustomComparator_thenListIsSortedCorrectly() {
List<String> movieNames = Arrays.asList("amazing SpiderMan", "Godzilla", "Sing", "Minions");
List<String> naturalSortOrder = Arrays.asList("Godzilla", "Minions", "Sing", "amazing SpiderMan");
List<String> comparatorSortOrder = Arrays.asList("amazing SpiderMan", "Godzilla", "Minions", "Sing");
Collections.sort(movieNames);
assertThat(movieNames).isEqualTo(naturalSortOrder);
Collections.sort(movieNames, Comparator.comparing(s -> s.toLowerCase()));
assertThat(movieNames).isEqualTo(comparatorSortOrder);
}
问题与解决:
- 默认排序:
["Godzilla", "Minions", "Sing", "amazing SpiderMan"]
(错误) - 自定义比较器:
["amazing SpiderMan", "Godzilla", "Minions", "Sing"]
(正确) - 关键技巧:使用
String::toLowerCase
作为排序键提取器
处理特殊字符
要求@
开头的字符串排在最后:
@Test
void givenListOfStringsIncludingSomeWithSpecialCharacter_whenCustomComparator_thenListIsSortedWithSpecialCharacterLast() {
List<String> listWithSpecialCharacters = Arrays.asList("@laska", "blah", "jo", "@sk", "foo");
List<String> sortedNaturalOrder = Arrays.asList("@laska", "@sk", "blah", "foo", "jo");
List<String> sortedSpecialCharacterLast = Arrays.asList("blah", "foo", "jo", "@laska", "@sk");
Collections.sort(listWithSpecialCharacters);
assertThat(listWithSpecialCharacters).isEqualTo(sortedNaturalOrder);
Comparator<String> specialSignComparator = Comparator.<String, Boolean>comparing(s -> s.startsWith("@"));
Comparator<String> specialCharacterComparator = specialSignComparator.thenComparing(Comparator.naturalOrder());
listWithSpecialCharacters.sort(specialCharacterComparator);
assertThat(listWithSpecialCharacters).isEqualTo(sortedSpecialCharacterLast);
}
实现要点:
- 默认排序:
["@laska", "@sk", "blah", "foo", "jo"]
(不符合需求) - 链式比较器:先按是否
@
开头分组,组内自然排序 - 最终结果:
["blah", "foo", "jo", "@laska", "@sk"]
使用Stream排序
Java 8 Stream API提供函数式排序方案。
自然顺序排序
@Test
void givenListOfStrings_whenSortWithStreams_thenListIsSortedInNaturalOrder() {
List<String> sortedList = INPUT_NAMES.stream()
.sorted()
.collect(Collectors.toList());
assertThat(sortedList).isEqualTo(EXPECTED_NATURAL_ORDER);
}
特性:
sorted()
返回按自然顺序排序的流- 要求元素实现
Comparable
逆序排序
使用Lambda定义比较器:
@Test
void givenListOfStrings_whenSortWithStreamsUsingComparator_thenListIsSortedInReverseOrder() {
List<String> sortedList = INPUT_NAMES.stream()
.sorted((element1, element2) -> element2.compareTo(element1))
.collect(Collectors.toList());
assertThat(sortedList).isEqualTo(EXPECTED_REVERSE_ORDER);
}
或使用内置比较器:
Comparator<String> reverseOrderComparator = Comparator.reverseOrder();
List<String> sortedList = INPUT_NAMES.stream()
.sorted(reverseOrder)
.collect(Collectors.toList());
使用TreeSet排序
TreeSet
基于红黑树实现,自动维护元素排序:
@Test
void givenNames_whenUsingTreeSet_thenListIsSorted() {
SortedSet<String> sortedSet = new TreeSet<>(INPUT_NAMES);
List<String> sortedList = new ArrayList<>(sortedSet);
assertThat(sortedList).isEqualTo(EXPECTED_NATURAL_ORDER);
}
注意事项:
- 自动去重,会丢失重复元素
- 仅适用于无重复值的场景
使用List的sort方法
List
接口自带的sort()
方法更简洁:
@Test
void givenListOfStrings_whenSortOnList_thenListIsSorted() {
INPUT_NAMES.sort(Comparator.reverseOrder());
assertThat(INPUT_NAMES).isEqualTo(EXPECTED_REVERSE_ORDER);
}
优势:直接在原列表操作,无需额外工具类。
本地化敏感排序
不同语言的排序规则差异显著,例如西班牙语:
List<String> accentedStrings = Arrays.asList("único", "árbol", "cosas", "fútbol");
默认排序结果:["cosas", "fútbol", "árbol", "único"]
(错误)
使用Collator
处理本地化排序:
Collator esCollator = Collator.getInstance(new Locale("es"));
accentedStrings.sort((s1, s2) -> {
return esCollator.compare(s1, s2);
});
正确结果:["árbol", "cosas", "fútbol", "único"]
完整测试用例:
@Test
void givenListOfStringsWithAccent_whenSortWithTheCollator_thenListIsSorted() {
List<String> accentedStrings = Arrays.asList("único", "árbol", "cosas", "fútbol");
List<String> sortedNaturalOrder = Arrays.asList("cosas", "fútbol", "árbol", "único");
List<String> sortedLocaleSensitive = Arrays.asList("árbol", "cosas", "fútbol", "único");
Collections.sort(accentedStrings);
assertThat(accentedStrings).isEqualTo(sortedNaturalOrder);
Collator esCollator = Collator.getInstance(new Locale("es"));
accentedStrings.sort((s1, s2) -> {
return esCollator.compare(s1, s2);
});
assertThat(accentedStrings).isEqualTo(sortedLocaleSensitive);
}
处理重音字符排序
Unicode中重音字符有多种编码方式,需特殊处理。
标准化后排序
使用Normalizer
规范化字符:
Collections.sort(accentedStrings, (o1, o2) -> {
o1 = Normalizer.normalize(o1, Normalizer.Form.NFD);
o2 = Normalizer.normalize(o2, Normalizer.Form.NFD);
return o1.compareTo(o2);
});
结果:["árbol", "cosas", "fútbol", "único"]
关键:NFD
形式将重音字符分解为基本字符+修饰符。
移除重音后排序
使用Apache Commons Lang的StringUtils
:
@Test
void givenListOfStrinsWithAccent_whenComparatorWithNormalizer_thenListIsNormalizedAndSorted() {
List<String> accentedStrings = Arrays.asList("único", "árbol", "cosas", "fútbol");
List<String> naturalOrderSorted = Arrays.asList("cosas", "fútbol", "árbol", "único");
List<String> stripAccentSorted = Arrays.asList("árbol","cosas", "fútbol","único");
Collections.sort(accentedStrings);
assertThat(accentedStrings).isEqualTo(naturalOrderSorted);
Collections.sort(accentedStrings, Comparator.comparing(input -> StringUtils.stripAccents(input)));
assertThat(accentedStrings).isEqualTo(stripAccentSorted);
}
使用基于规则的排序器
RuleBasedCollator
支持自定义排序规则:
@Test
void givenListofStrings_whenUsingRuleBasedCollator_thenListIsSortedUsingRuleBasedCollator() throws ParseException {
List<String> movieNames = Arrays.asList(
"Godzilla","AmazingSpiderMan","Smurfs", "Minions");
List<String> naturalOrderExpected = Arrays.asList(
"AmazingSpiderMan", "Godzilla", "Minions", "Smurfs");
Collections.sort(movieNames);
List<String> rulesBasedExpected = Arrays.asList(
"Smurfs", "Minions", "AmazingSpiderMan", "Godzilla");
assertThat(movieNames).isEqualTo(naturalOrderExpected);
String rule = "< s, S < m, M < a, A < g, G";
RuleBasedCollator rulesCollator = new RuleBasedCollator(rule);
movieNames.sort(rulesCollator);
assertThat(movieNames).isEqualTo(rulesBasedExpected);
}
规则解析:
<
表示小于关系- 规则顺序:s < m < a < g
- 结果:
["Smurfs", "Minions", "AmazingSpiderMan", "Godzilla"]
注意事项:
- 规则语法错误会抛出
ParseException
- 实现了
Comparator
接口,可直接用于排序
总结
本文系统梳理了Java中列表字母排序的多种方案:
方法 | 适用场景 | 优势 | 局限性 |
---|---|---|---|
Collections.sort() |
基础排序 | 简单直接 | 功能单一 |
Comparator |
自定义排序 | 灵活控制逻辑 | 需手动实现 |
Stream API | 函数式处理 | 链式操作 | 依赖Java 8+ |
TreeSet |
自动去重 | 自动维护顺序 | 丢失重复值 |
List.sort() |
原地排序 | 语法简洁 | 仅限List接口 |
Collator |
本地化排序 | 支持多语言 | 需指定Locale |
RuleBasedCollator |
复杂规则 | 高度定制化 | 规则定义复杂 |
实战建议:
- 简单场景优先用
Collections.sort()
- 特殊需求通过
Comparator
定制 - 国际化场景必用
Collator
- 复杂规则考虑
RuleBasedCollator
掌握这些技巧,轻松应对各种排序需求!