用Java中的Comparable和Comparator排序

程序员经常需要将数据库中的元素排序到集合,数组或映射中。 在Java中,我们可以实现任何类型的排序算法。 使用Comparable接口和compareTo()方法,我们可以使用字母顺序, String长度,反向字母顺序或数字进行排序。 Comparator界面允许我们以更灵活的方式执行相同操作。

无论我们想做什么,我们只需要知道如何为给定的接口和类型实现正确的排序逻辑即可。

获取源代码

获取此Java Challenger 的代码 。 在遵循示例的同时,您可以运行自己的测试。

用自定义对象对Java列表进行排序

在我们的示例中,我们将使用到目前为止与其他Java Challenger相同的POJO。 在第一个示例中,我们使用通用类型的SimpsonSimpson类中实现Comparable接口:


class Simpson implements Comparable<Simpson> {
    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<SimpsonCharacter> 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()方法将给定对象或当前实例与指定对象进行比较,以确定对象的顺序。 快速浏览compareTo()工作原理:

如果比较返回

然后 ...

>= 1

this.name > simpson.name

0

this.name == simpson.name

<= -1

this.name < simpson.name

我们只能使用与sort()方法相当的sort() 。 如果我们尝试传递未实现ComparableSimpson ,则会收到编译错误。

sort()方法通过传递Comparable任何对象来使用多态 。 然后将按预期对对象进行排序。

先前代码的输出为:


Bart Homer Lisa Marge 

如果我们想颠倒顺序,我们可以将sort()换成reverse() ; 从:


Collections.sort(simpsons);

至:


Collections.reverse(simpsons);

部署reverse()方法会将先前的输出更改为:


Marge Lisa Homer Bart 

排序Java数组

在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<com.javaworld.javachallengers.sortingcomparable.Simpson>)
    method java.util.Collections.<T>sort(java.util.List<T>) 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.<T>sort(java.util.List<T>,java.util.Comparator<? super T>) is not applicable
      (cannot infer type-variable(s) T
        (actual and formal argument lists differ in length))

该日志可能令人困惑,但请不要担心。 请记住,任何未实现Comparable接口的已排序对象都将引发ClassCastException

使用TreeMap对地图排序

Java API包括许多有助于排序的类,包括TreeMap 。 在下面的示例中,我们使用TreeMap将键排序到Map


public class TreeMapExample {

    public static void main(String... barney) {
        Map<SimpsonCharacter, String> 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使用Comparable接口实现的compareTo()方法。 生成的Map中的每个元素均按其键排序。 在这种情况下,输出为:


Barney=beer, Homer=television, Lenny=Carl, Moe=shotgun

但是请记住:如果对象未实现Comparable ,则将抛出ClassCastException

使用TreeSet对集合进行排序

Set接口负责存储唯一值,但是当我们使用TreeSet实现时,插入的元素将在添加它们时自动排序:


public class TreeSetExample {

    public static void main(String... barney) {
        Set<SimpsonCharacter> 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

用比较器排序

如果我们不想使用POJO类中的相同compareTo()方法怎么办? 我们可以重写Comparable方法以使用其他逻辑吗? 下面是一个示例:


public class BadExampleOfComparable {

    public static void main(String... args) {
        List<SimpsonCharacter> 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<SimpsonCharacter> characters = new ArrayList<>();

        SimpsonCharacter homer = new SimpsonCharacter("Homer");
        SimpsonCharacter moe = new SimpsonCharacter("Moe");

        characters.add(homer);
        characters.add(moe);

        Collections.sort(characters, (Comparator.<SimpsonCharacter>
                        comparingInt(character1 -> character1.name.length())
                        .thenComparingInt(character2 -> character2.name.length())));

        System.out.println(characters);
    }
}

这些示例说明了ComparableComparator之间的主要区别。

当对象有一个默认的默认比较时,请使用Comparable 。 使用Comparator时,你需要解决现有compareTo()或者当你需要使用特定的逻辑更灵活的方式。 Comparator从您的对象分离排序逻辑,并在sort()方法内包含compareTo()逻辑。

将Comparator与匿名内部类一起使用

在下一个示例中,我们使用匿名内部类比较对象的值。 在这种情况下, 匿名内部类是实现Comparator任何类。 使用它意味着我们不必实例化实现接口的命名类。 相反,我们在匿名内部类中实现了compareTo()方法。


public class MarvelComparator {

    public static void main(String... comparator) {
        List<String> marvelHeroes = new ArrayList<>();

        marvelHeroes.add("SpiderMan ");
        marvelHeroes.add("Wolverine ");
        marvelHeroes.add("Xavier ");
        marvelHeroes.add("Cyclops ");


        Collections.sort(marvelHeroes, new Comparator<String>() {
            @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表达式一起使用

匿名内部类非常冗长,这可能会导致我们的代码出现问题。 在Comparator界面中,我们可以使用lambda表达式来简化代码并使代码更易于阅读。 例如,我们可以更改此:


Collections.sort(marvel, new Comparator<String>() {
            @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中的Lambda表达式

了解有关Java中的lambda表达式和其他功能编程技术的更多信息。

核心Java类是否可比?

许多核心Java类和对象都实现Comparable接口,这意味着我们不必为这些类实现compareTo()逻辑。 以下是一些熟悉的示例:


public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence { ...
整数

public final class Integer extends Number implements Comparable<Integer> { …

public final class Double extends Number implements Comparable<Double> {...

还有很多。 我鼓励您探索Java核心类,以学习它们的重要模式和概念。

接受可比接口挑战!

通过弄清楚以下代码的输出来测试您学到了什么。 请记住,如果仅通过学习自己解决挑战,您将学得最好。 找到答案后,您可以检查以下答案。 您也可以运行自己的测试以完全吸收这些概念。


public class SortComparableChallenge {

    public static void main(String... doYourBest) {
        Set<Simpson> 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<Simpson> list = new ArrayList<>();
        list.addAll(set);
        Collections.reverse(list);
        list.forEach(System.out::println);
    }

    static class Simpson implements Comparable<Simpson> {
        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
       Marge

B)   Maggie
       Bart
       Lisa
       Marge
       Homer

C)   Marge
       Maggie
       Lisa
       Homer
       Bart

D)   Indeterminate

翻译自: https://www.infoworld.com/article/3323403/java-challengers-5-sorting-with-comparable-and-comparator-in-java.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaComparableComparator都是用于进行对象比较的接口。它们的用法如下: 1. Comparable接口 Comparable接口是Java内置的接口,它包含一个方法compareTo(),用于比较对象的大小。实现该接口的类可以直接进行排序。 例如,我们定义一个Person类实现Comparable接口: ``` public class Person implements Comparable<Person> { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public int compareTo(Person person) { // 按照年龄进行排序 return this.age - person.age; } } ``` 在这个例子,我们通过实现Comparable接口,并重写compareTo()方法,按照年龄进行排序。 使用Comparable接口进行排序的例子: ``` List<Person> list = new ArrayList<Person>(); list.add(new Person("Tom", 20)); list.add(new Person("Jerry", 18)); list.add(new Person("Jack", 25)); Collections.sort(list); for(Person p : list) { System.out.println(p.getName() + " " + p.getAge()); } ``` 输出结果: ``` Jerry 18 Tom 20 Jack 25 ``` 2. Comparator接口 Comparator接口也是Java内置的接口,它包含一个方法compare(),用于比较两个对象的大小。实现该接口的类可以定制不同的比较规则。 例如,我们定义一个PersonComparator类实现Comparator接口: ``` public class PersonComparator implements Comparator<Person> { public int compare(Person p1, Person p2) { // 按照姓名进行排序 return p1.getName().compareTo(p2.getName()); } } ``` 在这个例子,我们通过实现Comparator接口,并重写compare()方法,按照姓名进行排序。 使用Comparator接口进行排序的例子: ``` List<Person> list = new ArrayList<Person>(); list.add(new Person("Tom", 20)); list.add(new Person("Jerry", 18)); list.add(new Person("Jack", 25)); Collections.sort(list, new PersonComparator()); for(Person p : list) { System.out.println(p.getName() + " " + p.getAge()); } ``` 输出结果: ``` Jack 25 Jerry 18 Tom 20 ``` 总之,ComparableComparator都是用于对象比较的接口。使用Comparable接口可以方便地对实现该接口的对象进行排序,而使用Comparator接口可以定制不同的比较规则。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值