知识库 知识库
首页
  • Hyperskill - Java

    • Java basic
    • Java OOP
    • 应知
    • 扩展
    • IO & Stream
    • Error & Exception
    • Algorithm & Data structure
    • Design pattern
    • Web
    • Spring boot
  • 练习题

    • 选择题 & 填空题
    • 代码题
  • Frank - Java与生活 (OOP)

    • 参考资料
    • Java基础
    • OOP上半部分
    • OOP下半部分
  • Frank - Java API进阶

    • Base API
    • Unit Test and main function
  • 学习笔记
  • 学习笔记

    • 数据库
  • Frank - MySQL删库跑路

    • 安装、连接、配置
    • 基本操作——数据库
    • 基本操作——表
    • 基本操作——数据
    • 数据类型
    • 列属性完整性
    • 数据库设计思维
    • 单表查询
    • 多表查询
  • 学习笔记

    • 其它
  • Frank - Linux现代方法

    • 必知
    • 命令
    • 技巧
  • 技术文档
  • Git
  • GitHub技巧
  • 前端
  • Khan Academy - 语法
  • Monthly
  • 阅读
  • Others
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 标签
  • 归档
GitHub (opens new window)

Jim FuckPPT

Java小学生
首页
  • Hyperskill - Java

    • Java basic
    • Java OOP
    • 应知
    • 扩展
    • IO & Stream
    • Error & Exception
    • Algorithm & Data structure
    • Design pattern
    • Web
    • Spring boot
  • 练习题

    • 选择题 & 填空题
    • 代码题
  • Frank - Java与生活 (OOP)

    • 参考资料
    • Java基础
    • OOP上半部分
    • OOP下半部分
  • Frank - Java API进阶

    • Base API
    • Unit Test and main function
  • 学习笔记
  • 学习笔记

    • 数据库
  • Frank - MySQL删库跑路

    • 安装、连接、配置
    • 基本操作——数据库
    • 基本操作——表
    • 基本操作——数据
    • 数据类型
    • 列属性完整性
    • 数据库设计思维
    • 单表查询
    • 多表查询
  • 学习笔记

    • 其它
  • Frank - Linux现代方法

    • 必知
    • 命令
    • 技巧
  • 技术文档
  • Git
  • GitHub技巧
  • 前端
  • Khan Academy - 语法
  • Monthly
  • 阅读
  • Others
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 标签
  • 归档
GitHub (opens new window)
  • Hyperskill - Java

    • Java basic

    • Java OOP

    • 应知

      • Theory:Functional decomposition
      • Theory:Paradigms
      • Theory:Overloading
      • Theory:Write, compile, and run
      • Theory:Annotations basics
      • Theory:JVM, JRE, and JDK
      • Theory:Jave Archive
      • Theory:Running programs on your computer
      • Theory:Enums in Java
      • Theory:Fields and methods in enum
      • Theory:StringBuilder
      • Theory:Immutability
      • Theory:Boxing and unboxing
      • Theory:Introduction to generic programming
      • Theory:Generics and Object
      • Theory:What are collections
      • Theory:The collections Framework overview
      • Theory:ArrayList
      • Theory:The List interface
      • Theory:Comparable
        • Preparing to compare
        • Comparable interface
        • Implementing the compareTo method
        • Conclusion
      • Theory:Processing strings
      • Theory:Initialization blocks
      • Theory:Introduction to API
      • Theory:Generic methods
    • 扩展

    • IO & Stream

    • Error & Exception

    • Algorithm & Data structure

    • Design pattern

    • Web

    • Spring boot

  • 练习题

  • Frank - Java与生活

  • Frank - Java API进阶

  • 学习笔记

  • Java
  • Hyperskill - Java
  • 应知
Jim
2022-10-06
目录

Theory:Comparable

While working with data, you will likely need to order it in a convenient way. For example, you may have to put numbers in ascending order, group lines in alphabetical order, and organize anything you work with by date, by price, or by other custom characteristics.

In Java, it's possible to implement various sorting algorithms for any type of data. What if you have to work with custom types, sort elements of a collection, and try to compare objects that are not directly comparable? That's where the Comparable interface comes in handy. In this topic, we will learn all about this interface and its compareTo() method.

# Preparing to compare

Let's look at an example. We created a list of Integer's, added some elements and then sorted them.

public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    list.add(55);
    list.add(13);
    list.add(47);

    Collections.sort(list);
    System.out.println(list);
}
1
2
3
4
5
6
7
8
9

As expected, we get:

[13, 47, 55]
1

Now, let's create a simple class Carwhere we want to sort cars by their numbers.

public class Car {
    private int number;
    private String model;
    private String color;
    private int weight;

    // constructor

    // getters, setters
}
1
2
3
4
5
6
7
8
9
10

Now we try to write some code for the main method, create our collection and sort it using the Collections.sort() method.

public static void main(String[] args) {
    List<Car> cars = new ArrayList<>();
    Car car1 = new Car(876, "BMW", "white", 1400);
    Car car2 = new Car(345, "Mercedes", "black", 2000);
    Car car3 = new Car(470, "Volvo", "blue", 1800);
    cars.add(car1);
    cars.add(car2);
    cars.add(car3);

    Collections.sort(cars);
    System.out.println(cars);
}
1
2
3
4
5
6
7
8
9
10
11
12

As a result, we get a compilation error:

The method sort(List) in the type Collections 
  is not applicable for the arguments (ArrayList)
1
2

The reason for this is that standard classes like Integer, String and so on implement a special interface, so we can compare them without any problems. As for our custom class Car, it doesn't work like that. Let's see how we can fix this.

# Comparable interface

Comparable` provides the `compareTo()` method which allows comparing an object with other objects of the same type. It's also important to comply with the conditions: all objects can be compared to other objects of the same type in the most widely used way, which means `compareTo()` should be consistent with the `equals` method. A sequence of data has the **natural ordering**, if for each 2 elements `a` and `b`, where `a` is located to the left of `b`, the condition is true: `a.compareTo(b) <= 0
1

It's easy to understand how to compare an Integer or String because they already implement the Comparable interface, but how do we compare objects of our custom type? We can do it in different ways depending on the task. We can compare them by any single field or several fields.

To be able to sort, we must rewrite our Car class using the Comparable interface. For example, we can compare our Car objects by their number. Here's how you can implement it:

public class Car implements Comparable<Car> {

    private int number;
    private String model;
    private String color;
    private int weight;

    // constructor

    // getters, setters

    @Override
    public int compareTo(Car otherCar) {
        return Integer.valueOf(getNumber()).compareTo(otherCar.getNumber());
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Now if we run our new code we get the correct result.

# Implementing the compareTo method

Let's talk about the compareTo() method. It compares the current object with the object sent as a parameter. To implement it correctly we need to make sure that the method returns:

  • A positive integer (for example, 1), if the current object is greater;
  • A negative integer (for example, -1), if the current object is less;
  • Zero, if they are equal.

Below you can see an example of how the compareTo() method is implemented in the Integer class.

@Override
public int compareTo(Integer anotherInteger) {
    return compare(this.value, anotherInteger.value);
}

public static int compare (int x, int y) {
    return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
1
2
3
4
5
6
7
8

There are some other rules for implementing the compareTo() method. To demonstrate them, imagine we have a class called Coin:

class Coin implements Comparable<Coin> {
    private final int nominalValue;    // nominal value
    private final int mintYear;        // the year the coin was minted

    Coin(int nominalValue, int mintYear) {
        this.nominalValue = nominalValue;
        this.mintYear = mintYear;
    }

    @Override
    public int compareTo(Coin other) {
        // This method we have to implement
    }

    // We consider two coins equal if they have the same nominal value
    @Override
    public boolean equals(Object that) {
        if (this == that) return true;
        if (that == null || getClass() != that.getClass()) return false;
        Coin coin = (Coin) that;
        return nominalValue == coin.nominalValue;
    }

    // getters, setters, hashCode and toString
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

Let's create some objects of Coin.

public static void main(String[] args) {

    Coin big = new Coin(25, 2006);
    Coin medium1 = new Coin(10, 2016);
    Coin medium2 = new Coin(10, 2001);
    Coin small = new Coin(2, 2000);
}
1
2
3
4
5
6
7

One of the rules is to keep the compareTo() implementation consistent with the implementation of the equals() method. For example:

  • medium1.compareTo(medium2) == 0 should have the same boolean value as medium2.equals(medium1)

If we compare our coins and big is bigger than medium1 and medium1 is bigger than small, then big is bigger than small:

  • (big.compareTo(medium1) > 0 && medium1.compareTo(small) > 0) implies big.compareTo(small) > 0

big is bigger than small, hence small is smaller than big:

  • big.compareTo(small) > 0 and small.compareTo(big) < 0

if medium1 is equal to medium2, they both must be bigger or smaller than small and big respectively:

  • medium1.compareTo(medium2) == 0 implies that big.compareTo(medium1) > 0 and big.compareTo(medium2) > 0 or small.compareTo(medium1) < 0 and small.compareTo(medium2) < 0

This will ensure that we can safely use such objects in sorted sets and sorted maps.

In this case, we can comply with all these requirements if we compare our coins by their nominal value:

class Coin implements Comparable<Coin> {

    // fields, constructor, equals, hashCode, getters and setters

    @Override
    public int compareTo(Coin other) {
        if (nominalValue == other.nominalValue) {
            return 0;
        } else if (nominalValue < other.nominalValue) {
            return -1;
        } else {
            return 1;
        }
    }

    @Override
    public String toString() {
        return "Coin{nominal=" + nominalValue + ", year=" + mintYear + "}";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Now we can add the coins to a list and sort them:

List<Coin> coins = new ArrayList<>();

coins.add(big);
coins.add(medium1);
coins.add(medium2);
coins.add(small);

Collections.sort(coins);
coins.forEach(System.out::println);
1
2
3
4
5
6
7
8
9

In the output, we can see that the coins have been successfully sorted:

Coin{nominal=2, year=2000}
Coin{nominal=10, year=2016}
Coin{nominal=10, year=2001}
Coin{nominal=25, year=2006}
1
2
3
4

# Conclusion

In this topic, we explored how to use the Comparable interface in our custom classes to define natural ordering algorithms. We also learned how to properly implement the compareTo method.

编辑 (opens new window)
#Java#Collection
上次更新: 2022/10/12, 17:01:25
Theory:The List interface
Theory:Processing strings

← Theory:The List interface Theory:Processing strings→

最近更新
01
《挪威的森林》
04-14
02
青钢影
04-14
03
Processing strings
02-18
更多文章>
Theme by Vdoing | Copyright © 2022-2023 Jim Frank | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式