本文共 9649 字,大约阅读时间需要 32 分钟。
程序员经常需要将数据库中的元素排序到集合,数组或映射中。在Java中,我们可以实现任何类型的排序算法。使用Comparable接口和compareTo()方法,我们可以使用字母顺序,String长度,反向字母顺序或数字进行排序。该Comparator界面允许我们以更灵活的方式执行相同操作。
无论我们想做什么,我们只需要知道如何为给定的接口和类型实现正确的排序逻辑即可。
获取此Java Challenger 的代码(获取地址:https://github.com/rafadelnero/javaworld-challengers)。在遵循示例的同时,您可以运行自己的测试。
对于我们的示例,我们将使用到目前为止其他Java Challengers所使用的POJO。在第一个示例中,我们使用通用类型在类中实现Comparable接口:SimpsonSimpson
[ 在这个由12部分组成的综合课程中,从入门概念到高级设计模式学习Java!]
class Simpson implements Comparable { String name; Simpson(String name) { this.name = name; } @Override public int compareTo(Simpson simpson) { return this.name.compareTo(simpson.name); }}public class SimpsonSorting { public static void main(String... sortingWithList) { List simpsons = new ArrayList<>(); simpsons.add(new SimpsonCharacter("Homer ")); simpsons.add(new SimpsonCharacter("Marge ")); simpsons.add(new SimpsonCharacter("Bart ")); simpsons.add(new SimpsonCharacter("Lisa ")); Collections.sort(simpsons); simpsons.stream().map(s -> s.name).forEach(System.out::print); Collections.reverse(simpsons); simpsons.stream().forEach(System.out::print); }}
请注意,我们已经覆盖了compareTo()方法并传递了另一个Simpson对象。我们也重写了该toString()方法,只是为了使示例易于阅读。
该toString方法显示来自对象的所有信息。当我们打印对象时,输出将是中实现的toString()。
该compareTo()方法将给定对象或当前实例与指定对象进行比较,以确定对象的顺序。快速浏览一下compareTo()工作原理:
我们只能使用与该sort()方法相当的类。如果我们尝试传递Simpson未实现的Comparable,则会收到编译错误。
该sort()方法通过传递任何对象来使用多态Comparable。然后将按预期对对象进行排序。
先前代码的输出为:
Bart Homer Lisa Marge
如果我们想颠倒顺序,我们可以交换sort()的reverse(); 从:
Collections.sort(simpsons);
至:
Collections.reverse(simpsons);
部署该reverse()方法会将先前的输出更改为:
Marge Lisa Homer Bart
在Java中,我们可以对数组进行排序,只要它实现Comparable接口即可。这是一个例子:
public class ArraySorting { public static void main(String... moeTavern) { int[] moesPints = new int[] {9, 8, 7, 6, 1}; Arrays.sort(moesPints); Arrays.stream(moesPints).forEach(System.out::print); Simpson[] simpsons = new Simpson[]{new Simpson("Lisa"), new Simpson("Homer")}; Arrays.sort(simpsons); Arrays.stream(simpsons).forEach(System.out::println); }}
在第一次sort()调用中,数组被排序为:
1 6 7 8 9
在第二个sort()调用中,它被排序为:
Homer Lisa
请记住,自定义对象必须实现Comparable才能排序,即使是数组也是如此。
如果Simpson对象未实现Comparable,则将引发ClassCastException。如果将其作为测试运行,您将看到类似以下输出的内容:
Error:(16, 20) java: no suitable method found for sort(java.util.List) method java.util.Collections.sort(java.util.List) is not applicable (inference variable T has incompatible bounds equality constraints: com.javaworld.javachallengers.sortingcomparable.Simpson lower bounds: java.lang.Comparable super T>) method java.util.Collections.sort(java.util.List,java.util.Comparator super T>) is not applicable (cannot infer type-variable(s) T (actual and formal argument lists differ in length))
该日志可能令人困惑,但请不要担心。请记住,ClassCastException任何未实现该Comparable接口的已排序对象都将抛出a 。
Java API包括许多有助于排序的类,包括TreeMap。在下面的示例中,我们用于TreeMap将键排序为Map。
public class TreeMapExample { public static void main(String... barney) { Map simpsonsCharacters = new TreeMap<>(); simpsonsCharacters.put(new SimpsonCharacter("Moe"), "shotgun"); simpsonsCharacters.put(new SimpsonCharacter("Lenny"), "Carl"); simpsonsCharacters.put(new SimpsonCharacter("Homer"), "television"); simpsonsCharacters.put(new SimpsonCharacter("Barney"), "beer"); System.out.println(simpsonsCharacters); }}
TreeMap使用接口compareTo()实现的方法Comparable。结果中的每个元素Map均按其键排序。在这种情况下,输出为:
Barney=beer, Homer=television, Lenny=Carl, Moe=shotgun
但是请记住:如果对象未实现Comparable,ClassCastException则将抛出a。
该Set接口负责存储唯一值,但是当我们使用TreeSet实现时,插入的元素将在我们添加它们时自动排序:
public class TreeSetExample { public static void main(String... barney) { Set simpsonsCharacters = new TreeSet<>(); simpsonsCharacters.add(new SimpsonCharacter("Moe")); simpsonsCharacters.add(new SimpsonCharacter("Lenny")); simpsonsCharacters.add(new SimpsonCharacter("Homer")); simpsonsCharacters.add(new SimpsonCharacter("Barney")); System.out.println(simpsonsCharacters); }}
此代码的输出是:
Barney, Homer, Lenny, Moe
同样,如果我们使用的不是Comparable,ClassCastException则将抛出a。
如果我们不想使用compareTo()POJO类中的相同方法怎么办?我们可以覆盖该Comparable方法以使用其他逻辑吗?下面是一个示例:
public class BadExampleOfComparable { public static void main(String... args) { List characters = new ArrayList<>(); SimpsonCharacter homer = new SimpsonCharacter("Homer") { @Override public int compareTo(SimpsonCharacter simpson) { return this.name.length() - (simpson.name.length()); } }; SimpsonCharacter moe = new SimpsonCharacter("Moe") { @Override public int compareTo(SimpsonCharacter simpson) { return this.name.length() - (simpson.name.length()); } }; characters.add(homer); characters.add(moe); Collections.sort(characters); System.out.println(characters); }}
如您所见,此代码很复杂,并且包含很多重复。compareTo()对于相同的逻辑,我们不得不重写该方法两次。如果还有更多元素,我们将不得不为每个对象复制逻辑。
幸运的是,我们具有Comparator接口,该接口使我们可以将compareTo()逻辑与Java类分离。考虑上面使用重写的同一示例Comparator:
public class GoodExampleOfComparator { public static void main(String... args) { List characters = new ArrayList<>(); SimpsonCharacter homer = new SimpsonCharacter("Homer"); SimpsonCharacter moe = new SimpsonCharacter("Moe"); characters.add(homer); characters.add(moe); Collections.sort(characters, (Comparator. comparingInt(character1 -> character1.name.length()) .thenComparingInt(character2 -> character2.name.length()))); System.out.println(characters); }}
这些示例说明了Comparable和之间的主要区别Comparator。
使用Comparable时,有你的对象单一,默认的比较。使用Comparator时,你需要要解决现有的compareTo(),或者当你需要使用特定的逻辑更灵活的方式。Comparator将排序逻辑与对象分离,并将逻辑包含compareTo()在sort()方法中。
在下一个示例中,我们使用匿名内部类比较对象的值。一个匿名内部类,在这种情况下,任何类,它实现Comparator。使用它意味着我们不必实例化实现接口的命名类。相反,我们compareTo()在匿名内部类内部实现该方法。
public class MarvelComparator { public static void main(String... comparator) { List marvelHeroes = new ArrayList<>(); marvelHeroes.add("SpiderMan "); marvelHeroes.add("Wolverine "); marvelHeroes.add("Xavier "); marvelHeroes.add("Cyclops "); Collections.sort(marvelHeroes, new Comparator() { @Override public int compare(String hero1, String hero2) { return hero1.compareTo(hero2); } }); Collections.sort(marvelHeroes, (m1, m2) -> m1.compareTo(m2)); Collections.sort(marvelHeroes, Comparator.naturalOrder()); marvelHeroes.forEach(System.out::print); }}
一个匿名内部类是单纯的任何类,它的名称并不重要,并实现我们宣布的接口。因此,在示例中,新Comparator的实际上是一个没有名称的类的实例化,该类使用所需的逻辑来实现该方法。
匿名内部类非常冗长,这可能会导致我们的代码出现问题。在Comparator界面中,我们可以使用lambda表达式简化并简化代码。例如,我们可以更改此:
Collections.sort(marvel, new Comparator() { @Override public int compare(String hero1, String hero2) { return hero1.compareTo(hero2); } });
对此:
Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2));
更少的代码和相同的结果!
该代码的输出为:
Cyclops SpiderMan Wolverine Xavier
通过更改此代码,我们可以使代码更简单:
Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2));
对此:
Collections.sort(marvel, Comparator.naturalOrder());
许多核心Java类和对象都实现了该Comparable接口,这意味着我们不必compareTo()为这些类实现逻辑。以下是一些熟悉的示例:
public final class String implements java.io.Serializable, Comparable, CharSequence { ...
public final class Integer extends Number implements Comparable { …
public final class Double extends Number implements Comparable {...
还有很多。我鼓励您探索Java核心类,以学习它们的重要模式和概念。
通过弄清楚以下代码的输出来测试您学到了什么。请记住,如果仅通过学习自己解决挑战,就会学得最好。找到答案后,您可以检查以下答案。您也可以运行自己的测试以完全吸收这些概念。
public class SortComparableChallenge { public static void main(String... doYourBest) { Set set = new TreeSet<>(); set.add(new Simpson("Homer")); set.add(new Simpson("Marge")); set.add(new Simpson("Lisa")); set.add(new Simpson("Bart")); set.add(new Simpson("Maggie")); List list = new ArrayList<>(); list.addAll(set); Collections.reverse(list); list.forEach(System.out::println); } static class Simpson implements Comparable { String name; public Simpson(String name) { this.name = name; } public int compareTo(Simpson simpson) { return simpson.name.compareTo(this.name); } public String toString() { return this.name; } }}
A) Bart Homer Lisa Maggie MargeB) Maggie Bart Lisa Marge HomerC) Marge Maggie Lisa Homer BartD) Indeterminate
查看代码,您应该注意到的第一件事是我们正在使用TreeSet,因此元素将自动排序。第二件事是比较的顺序颠倒了,所以排序将以相反的顺序进行。
当我们第一次将元素添加到列表中时,会TreeSet自动将它们排序为:
MargeMaggieLisaHomerBart
然后,我们使用该reverse()方法,该方法可以反转元素的顺序。因此,最终输出将是:
BartHomerLisaMaggieMarge
转载地址:http://paodl.baihongyu.com/