1. Introduction
1.绪论
In this tutorial, we’ll explore various ways of sorting a list alphabetically in Java.
在本教程中,我们将探讨在Java中按字母顺序对列表进行排序的各种方法。
First, we’ll start with the Collections class and then use the Comparator interface. We’ll also use List’s API to sort alphabetically followed by the streams and finally use TreeSet.
首先,我们将从Collections类开始,然后使用Comparator 接口。我们还将使用List的API来按字母顺序排序,然后使用streams,最后使用TreeSet。
Additionally, we’ll be expanding our examples to explore several different scenarios, including sorting lists based on a specific locale, sorting accented lists, and using RuleBasedCollator to define our custom sorting rules.
此外,我们将扩展我们的示例,以探索几种不同的情况,包括根据特定的地域对列表进行排序,对带口音的列表进行排序,以及使用RuleBasedCollator>来定义我们自定义的排序规则。
2. Sorting Using Collections Class
2.使用Collections类进行排序
First, let’s see how we can sort a list using the Collections class.
首先,让我们看看如何使用Collections类对一个列表进行排序。
The Collections class provides a static, overloaded method sort that can take the list and sort it either in natural order or we can provide the custom sorting logic using a Comparator.
Collections类提供了一个静态的、重载的方法sort,它可以接受列表并按自然顺序排序,或者我们可以使用Comparator提供自定义排序逻辑。
2.1. Sorting in Natural/Lexicographic Order
2.1.按自然/词汇顺序排序
First, we’ll define the input list:
首先,我们要定义输入列表。
private static List<String> INPUT_NAMES = Arrays.asList("john", "mike", "usmon", "ken", "harry");
Now we’ll use the Collections class first to sort the list in the natural order, also known as lexicographic order:
现在我们先用Collections类对列表进行自然排序,也称为lexicographic order。
@Test
void givenListOfStrings_whenUsingCollections_thenListIsSorted() {
Collections.sort(INPUT_NAMES);
assertThat(INPUT_NAMES).isEqualTo(EXPECTED_NATURAL_ORDER);
}
where the EXPECTED_NATURAL_ORDER is:
其中EXPECTED_NATURAL_ORDER是。
private static List<String> EXPECTED_NATURAL_ORDER = Arrays.asList("harry", "john", "ken", "mike", "usmon");
Some important points to note here are:
这里需要注意的一些要点是。
- The list is sorted in ascending order according to the natural ordering of its elements
- We can pass any Collection to sort method whose elements are Comparable (implement Comparable interface)
- Here, we are passing a List of String class which is a Comparable class, and hence sorting works
- Collection class changes the state of the List object being passed to sort. Therefore we don’t need to return the list
2.2. Sorting in Reverse Order
2.2.反序排序
Let’s see how we can sort the same list in reverse alphabetical order.
让我们看看如何将同一个列表按字母顺序反向排序。
Let’s use the sort method again but now provide a Comparator:
让我们再次使用sort方法,但现在提供一个Comparator。
Comparator<String> reverseComparator = (first, second) -> second.compareTo(first);
Alternately we can simply use this static method from the Comparator interface:
另外,我们可以简单地使用Comparator接口的这个静态方法。
Comparator<String> reverseComparator = Comparator.reverseOrder();
Once we have the reverse Comparator, we can simply pass it to sort:
一旦我们有了反向比较器,我们就可以简单地把它传递给sort。
@Test
void givenListOfStrings_whenUsingCollections_thenListIsSortedInReverse() {
Comparator<String> reverseComparator = Comparator.reverseOrder();
Collections.sort(INPUT_NAMES, reverseComparator);
assertThat(INPUT_NAMES).isEqualTo(EXPECTED_REVERSE_ORDER);
}
where the EXPECTED_REVERSE_ORDER is:
其中EXPECTED_REVERSE_ORDER为。
private static List<String> EXPECTED_REVERSE_ORDER = Arrays.asList("usmon", "mike", "ken", "john", "harry");
A few relevant takeaways from this are:
从中得到的一些相关启示是。
- since the String class is final, we cannot extend and override the compareTo method of the Comparable interface for reverse sorting
- we can use the Comparator interface to implement a customized sorting strategy which is sorting in descending alphabetical order
- since Comparator is a functional interface, we can use a lambda expression
3. Custom Sorting Using Comparator Interface
3.使用比较器接口进行自定义排序
Often, we have to sort a list of Strings that need some custom sorting logic. That’s when we implement the Comparator interface and provide our desired sorting criteria.
通常,我们必须对需要一些自定义排序逻辑的字符串列表进行排序。这时我们要实现Comparator接口,并提供我们想要的排序标准。
3.1. Comparator to Sort List With Upper and Lower Case Strings
3.1.比较器对具有大写和小写字符串的列表进行排序
A typical scenario that can call for a custom sort can be a mixed list of Strings, starting with upper and lower cases.
一个可以要求自定义排序的典型场景是一个混合的字符串列表,以大写和小写开始。
Let’s set up and test this scenario:
让我们来设置和测试这个场景。
@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);
}
Note here:
这里要注意。
- the result of the sort before Comparator will be incorrect: [“Godzilla, “Minions” “Sing”, “amazing SpiderMan”]
- String::toLowerCase is the key extractor that extracts a Comparable sort key from a type String and returns a Comparator<String>
- the correct result after the sort with a custom Comparator will be: [“amazing SpiderMan”, “Godzilla”, “Minions”, “Sing”]
3.2. Comparator to Sort Special Characters
3.2.比较器对特殊字符进行排序
Let’s consider another example of a list with some names starting with a special character ‘@’.
让我们考虑另一个例子,即有一些名字以特殊字符’@’开头的列表。
We want them to be sorted at the end of the list, and the rest should be sorted in the natural order:
我们希望它们被排序在列表的最后,其余的应该按自然顺序排序。
@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);
}
Finally, some key points are:
最后,一些关键点是。
- The output of the sort without the Comparator is: [“@alaska”, “@ask”, “blah”, “foo”, “jo”] which is not correct for our scenario
- Like before, we have a key extractor that extracts a Comparable sort key from a type String and returns a specialCharacterLastComparator
- We can chain the specialCharacterLastComparator do a secondary sort using thenComparing expecting another Comparator
- Comparator.naturalOrder compares Comparable objects in the natural order.
- final output after the Comparator in the sort is : [“blah”, “foo”, “jo”, “@alaska”, “@ask”]
4. Sorting Using Streams
4.使用流进行排序
Now, let’s use Java 8 Streams to sort the list of String.
现在,让我们使用Java 8 Streams来对String的列表进行排序。
4.1. Sorting in Natural Order
4.1.按自然顺序排序
Let’s sort in the natural order first:
我们先按自然顺序排序。
@Test
void givenListOfStrings_whenSortWithStreams_thenListIsSortedInNaturalOrder() {
List<String> sortedList = INPUT_NAMES.stream()
.sorted()
.collect(Collectors.toList());
assertThat(sortedList).isEqualTo(EXPECTED_NATURAL_ORDER);
}
Important, here the sorted method returns a stream of String sorted according to the natural order.
重要的是,这里的sorted方法返回一个按照自然顺序排序的String流。
Additionally, elements of this Stream are Comparable.
另外,该流的元素是可比较的。
4.2. Sorting in Reverse Order
4.2.反序排序
Next, let’s pass a Comparator to sorted, which defines the reverse sorting strategy:
接下来,让我们将一个Comparator传递给sorted,它定义了反向排序策略。
@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);
}
Note, here we are using the Lamda function to define the Comparator
注意,这里我们使用Lamda函数来定义比较器。
Alternately, we can get the same Comparator:
另外,我们也可以得到同样的比较器。
Comparator<String> reverseOrderComparator = Comparator.reverseOrder();
Then we’ll simply pass this reverseOrderComparator to sorted:
然后我们将简单地把这个reverseOrderComparator传递给sorted。
List<String> sortedList = INPUT_NAMES.stream()
.sorted(reverseOrder)
.collect(Collectors.toList());
5. Sorting Using TreeSet
5.使用TreeSet进行排序
TreeSet stores object in sorted and ascending order using a Comparable interface.
TreeSet使用Comparable接口以排序和升序方式存储对象。
We can simply convert our unsorted list into TreeSet and then collect it back as a List:
我们可以简单地将未排序的列表转换为TreeSet,然后将其作为List>收集回来:。
@Test
void givenNames_whenUsingTreeSet_thenListIsSorted() {
SortedSet<String> sortedSet = new TreeSet<>(INPUT_NAMES);
List<String> sortedList = new ArrayList<>(sortedSet);
assertThat(sortedList).isEqualTo(EXPECTED_NATURAL_ORDER);
}
A constraint of this approach is that our original list shouldn’t have any duplicate values that it wants to preserve in the sorted list.
这种方法的一个约束条件是,我们的原始列表不应该有任何重复的值,它希望在排序后的列表中保留这些值t。
6. Sorting Using sort on List
6.使用sort对List进行排序
We can also sort a list using the sort method of the List:
我们还可以使用List的sort方法对一个列表进行排序。
@Test
void givenListOfStrings_whenSortOnList_thenListIsSorted() {
INPUT_NAMES.sort(Comparator.reverseOrder());
assertThat(INPUT_NAMES).isEqualTo(EXPECTED_REVERSE_ORDER);
}
Note that the sort method sorts this list according to the order dictated by the specified Comparator.
请注意,sort方法是根据指定的比较器所决定的顺序来排序这个列表的。
7. Locale Sensitive List Sorting
7.对当地语言敏感的列表排序
Depending on the locale, the result of the alphabetical sorting can vary due to language rules.
由于语言规则的原因,按字母顺序排序的结果可能有所不同。
Let’s take an example of a List of String:
让我们举一个List的String例子。
List<String> accentedStrings = Arrays.asList("único", "árbol", "cosas", "fútbol");
Let’s sort them as normal first:
让我们先把它们按正常情况分类。
Collections.sort(accentedStrings);
The output would be: [“cosas”, “fútbol”, “árbol”, “único”].
输出将是:[“东西”、”足球”、”树”、”独特”]。
However, we want them to be sorted using specific language rules.
然而,我们希望它们能够使用特定的语言规则进行排序。
Let’s create an instance of a Collator with the specific locale:
让我们创建一个具有特定locale的Collator实例。
Collator esCollator = Collator.getInstance(new Locale("es"));
Note, here we created an instance of Collator using es locale.
注意,在这里我们使用es区域设置创建了一个Collator的实例。
We can then pass this Collator as a Comparator being used for sorting either on the list, Collection, or using Streams:
然后我们可以将这个Collator作为一个Comparator被用来在list、Collection或使用Streams:进行排序。
accentedStrings.sort((s1, s2) -> {
return esCollator.compare(s1, s2);
});
The result of the sort would now be: [árbol, cosas, fútbol, único].
现在的排序结果将是。[árbol, cosas, fútbol, único].
Finally, let’s show this all together:
最后,让我们一起展示这一切。
@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);
}
8. Sorting List With Accented Characters
8.用重音字对列表进行排序
Characters with accents or other adornments can be encoded in several different ways in Unicode and thus sorted differently.
带有重音或其他装饰的字符在Unicode中可以用几种不同的方式进行编码,因此排序也不同。
We can either normalize the accented characters before sorting or can remove the accents from the characters.
我们可以在排序前将重音字符正常化,也可以将重音字符移除。
Let’s look into both of these ways of sorting accented lists.
让我们来看看这两种重音列表的排序方式。
8.1. Normalize Accented List and Sort
8.1.归一化重音列表和排序
To sort accurately such a list of Strings, let’s normalize accented characters using java.text.Normalizer
为了准确地对这样的字符串列表进行排序,让我们使用java.text.Normalizer对重音字符进行标准化处理。
Let’s start with a list of accented Strings:
让我们从重音字符串的列表开始。
List<String> accentedStrings = Arrays.asList("único","árbol", "cosas", "fútbol");
Next, let’s define a Comparator with a Normalize function and pass it to our sort method:
接下来,让我们定义一个带有Normalize函数的Comparator,并将其传递给我们的sort方法。
Collections.sort(accentedStrings, (o1, o2) -> {
o1 = Normalizer.normalize(o1, Normalizer.Form.NFD);
o2 = Normalizer.normalize(o2, Normalizer.Form.NFD);
return o1.compareTo(o2);
});
The sorted list after normalization will be: [árbol, cosas, fútbol, único]
归一化后的排序列表将是。[árbol, cosas, fútbol, único]
Importantly, here we are normalizing data using the form Normalizer.Form.NFD uses Canonical decomposition for accented characters. There are a few other forms that can also be used for normalization.
重要的是,这里我们使用Normalizer.Form.NFD的形式对数据进行规范化处理,对重音字符使用Canonical分解。还有一些其他形式也可以用于规范化。
8.2. Strip Accents and Sort
8.2.剥离重音和排序
Let’s use the StringUtils stripAccents method in the Comparator and pass it to the sort method:
让我们在Comparator中使用StringUtils stripAccents方法,并将其传递给sort方法。
@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).isEqualTostripAccentSorted);
}
9. Sorting Using Rule-Based Collator
9.使用基于规则的整理器进行排序
We can also define our custom rules to sort alphabetically using java text.RuleBasedCollator.
我们还可以使用java text.RuleBasedCollator.定义我们的自定义规则,按字母顺序排序。
Here, let’s define our custom sorting rule and pass it to RuleBasedCollator:
在这里,让我们定义我们的自定义排序规则,并将其传递给RuleBasedCollator。
@Test void givenListofStrings_whenUsingRuleBasedCollator_then ListIsSortedUsingRuleBasedCollator() 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);
}
Some of the important points to consider are:
需要考虑的一些重要问题是。
- RuleBasedCollator maps characters to sort keys
- the rule defined above is of form <relations> but there are other forms too in rules
- character s is smaller than m, and M is smaller than a, where A is smaller than g as per the rule definition
- a format exception will be thrown if the build process of the rules fails
- RuleBasedCollator implements the Comparator interface, hence can be passed to sort
10. Conclusion
10.结语
In this article, we looked into various techniques for sorting lists alphabetically in java.
在这篇文章中,我们研究了java中按字母顺序排列列表的各种技术。
First, we used Collections, then we explained the Comparator interface with some common examples.
首先,我们使用了Collections,然后我们用一些常见的例子解释了Comparator接口。
After Comparator, we looked into the sort method of the List followed by TreeSet.
在Comparator之后,我们研究了List的sort方法,然后是TreeSet。
We also explored how we can handle sorting the list of String in a different locale, normalizing and sorting accented lists, as well as the use of RuleBasedCollator with custom rules
我们还探讨了如何处理不同地区的String列表的排序,规范化和重音列表的排序,以及使用RuleBasedCollator与自定义规则的使用。
As always complete source code for this article can be found over on GitHub.
像往常一样,本文的完整源代码可以在GitHub上找到over。