虽然这种方法有一些准备工作并且相对较长,但它在小列表中的表现相当不错,在非常大的列表中表现得非常好。它的主要优点是:
- 它即时计算频率计数,每个单词仅计算一次。
- 唯一的排序是在单词列表本身上完成的。
- 比较器内不会重复额外的计算。
所以首先建立一个比较器。
- 首先比较数组中第一个位置的值(
a 计数)。
- 然后使用
thenComparing 附加下一个计数的每个可能位置
- 最后,反转整个比较器以倒序排序。
Comparator<int[]> comp = Comparator.comparing(a -> a[0]);
for (int i = 1; i < 26; i++) {
final int g = i;
comp = comp.thenComparing(a -> a[g]);
}
comp = comp.reversed();
现在定义一个Function 来获取每个单词的字母频率计数。此函数接受一个单词并返回一个包含 26 个值的 int 数组,用于每个单词中字母 a-z 的计数。计数不区分大小写。
Function<String, int[]> frequency = (word) -> {
int[] counts = new int[26];
for (char ch : word.toCharArray()) {
counts[Character.toLowerCase(ch)
- 'a']++;
}
return counts;
};
现在该排序了。把它们放在一起:
- 串流单词
- 为每个单词创建一个
Entry,其中key 是该单词,value 是为该单词计算的频率计数。
- 然后使用之前构建的比较器对
Entry#value 进行排序。
- 并映射到键,并以列表形式返回。
List<String> words = List.of("Banana", "Isle", "Best", "baha", "baby","bad", "animal", "zoo", "elephant");
List<String> result = words.stream().map(
word -> new AbstractMap.SimpleEntry<String, int[]>(
word, frequency.apply(word)))
.sorted(Entry.comparingByValue(comp))
.map(Entry::getKey).toList();
System.out.println(result);
打印
[Banana, baha, animal, baby, bad, elephant, Best, Isle, zoo]