10-比较器

  1. 重新认识 Arrays 类
  2. 两种比较器的使用
  3. 数据结构——二叉树(Binary Tree)

Arrays 类

java.util.Arrays.sort()可以实现数组的排序。

  1. 这个类里存在二分查找法
public static int binarySearch(long[] a, long key) {}
  1. 还提供了数组比较,但是要想判断数组是否相同,需要顺序完全一致:
import java.util.Arrays;

class Main {
    public static void main(String [] args){
        int dataA[] = new int[] {1,2,3};
        int dataB[] = new int[] {2,1,3};
        System.out.println(Arrays.equals(dataA, dataB));
        Arrays.sort(dataB);
        System.out.println(Arrays.equals(dataA, dataB));
    }
}

out:
false
true
  1. 填充数组:

    public static void fill(T[] a, T val) {}
    
  2. 将数组变为字符串输出:

    public static String toString(T[] a) {}
    

范例:

import java.util.Arrays;

class Main {
    public static void main(String [] args){
        int data[] = new int[10];
        Arrays.fill(data, 3);
        System.out.println(Arrays.toString(data));
    }
}

out:
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3]

基操,比较少出现。

比较器:Comparable(核心)

Arrays 类里面可以直接利用 sort()方法实现对象数组的排序。

import java.util.Arrays;

class Book{
    private String name;
    private double price;

    public Book(String name , double price){
        this.name=name;
        this.price=price;
    }

    @Override
    public String toString() {
        return "书名:" + this.name + ",价格:" + this.price;
    }
}

class Main {
    public static void main(String [] args){
        Book [] books = new Book[]{
                new Book("1", 1),
                new Book("2", 2),
                new Book("3", 3),
                new Book("4", 4)
            };
        Arrays.sort(books);
        System.out.println(Arrays.toString(books));
    }
}


out:
Exception in thread "main" java.lang.ClassCastException: Book cannot be cast to java.lang.Comparable
	at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
	at java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
	at java.util.Arrays.sort(Arrays.java:1246)
	at Main.main(Main.java:26)

此类异常的原因只有一个:两个没有关系的对象发生了强制性的转换。

每一个对象实际上值保留有地址信息,地址里面有内容。如果普通 int 型数组比较大小就够了。如果是对象数组,里面包含的如果只是编码(地址),比较是没有意义的。

因此要实现 Comparable 接口:

public interface Comparable<T> {
    public int compareTo(T o);
}

String 类就是 Comparable 的子类。

建议覆写 compareTo()返回三种数据:

  • 大于:1
  • 等于:0
  • 小于:-1

实现比较

import java.util.Arrays;

class Book implements Comparable<Book> {
    private String name;
    private double price;

    public Book(String name, double price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "书名:" + this.name + ",价格:" + this.price;
    }

    @Override
    public int compareTo(Book o) {
        // Arrays.sort()会自动调用此方法进行比较
        if (this.price > o.price) {
            return 1;
        } else if (this.price < o.price) {
            return -1;
        } else return 0;
    }
}

class Main {
    public static void main(String[] args) {
        Book[] books = new Book[]{
                new Book("1", 1),
                new Book("2", 2),
                new Book("3", 3),
                new Book("4", 4)
        };
        Arrays.sort(books);
        System.out.println(Arrays.toString(books));
    }
}

Arrays.sort()会自动调用 compareTo()方法进行比较。

数据结构——BinaryTree

树是比链表更复杂的动态数组。

二叉排序树,每个节点的左子树的值总比节点小,右子树的值总比节点大

中序遍历会得到有序序列。

实现二叉树:

  • step1:实现数据类:
class Book implements Comparable<Book> {
    private String name;
    private double price;

    public Book(String name, double price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "书名:" + this.name + ",价格:" + this.price;
    }

    @Override
    public int compareTo(Book o) {
        // Arrays.sort()会自动调用此方法进行比较
        if (this.price > o.price) {
            return 1;
        } else if (this.price < o.price) {
            return -1;
        } else return 0;
    }
}
  • 定义二叉树,所有数据结构都要有 Node 类的支持
class BinaryTree{
    private class Node{
        private Comparable data;
        private Node left;
        private Node right;
        public Node(Comparable data){
            this.data = data;
        }
        public void addNode(Node newNode){
            if (this.data.compareTo(newNode.data) > 0){ // 升降序调节
                if (this.left == null){
                    this.left = newNode;
                }else {
                    this.left.addNode(newNode);
                }
            }else {
                if (this.right == null){
                    this.right = newNode;
                }else {
                    this.right.addNode(newNode);
                }
            }
        }

        public void toArrayNode() {
            if (this.left!=null){
                this.left.toArrayNode();
            }
            BinaryTree.this.retData[BinaryTree.this.foot++] = this.data;
            if (this.right!=null){
                this.right.toArrayNode();
            }
        }
    }
    private Node root; //定义根节点
    private int count;
    private Object[] retData;
    private int foot;
    public void add (Object obj){
        Comparable com = (Comparable) obj;
        Node newNode = new Node(com);
        if (this.root == null){
            this.root = newNode;
        }else{
            this.root.addNode(newNode);
        }
        this.count++;
    }

    public Object[] toArray(){
        if (this.root == null){
            return null;
        }
        this.foot=0;
        this.retData = new Object[this.count];
        this.root.toArrayNode();
        return this.retData;
    }
}

二叉树中所有操作符合中序遍历!

  • 最后实现主函数:
class Main {
    public static void main(String[] args) {
        BinaryTree bt = new BinaryTree();
        bt.add(new Book("test1", 10));
        bt.add(new Book("test2", 30));
        bt.add(new Book("test3", 50));
        bt.add(new Book("test4", 5));
        System.out.println(Arrays.toString(bt.toArray()));
    }
}

out:
[书名:test4,价格:5.0, 书名:test1,价格:10.0, 书名:test2,价格:30.0, 书名:test3,价格:50.0]

挽救的比较器:Comparator

Comparable 接口需要在类定义的时候就实现了,如果要在后期实现比较就要使用 Comparator(不修改原有的类)

java.util.Comparator

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

因此只需要覆写 compare 方法。(equals 方法是 Object 类所拥有的)

需要重新编写一个工具类来实现比较!

实现比较工具

class BookComparator implements Comparator<Book>{
    @Override
    public int compare(Book o1, Book o2) {
        if (o1.getPrice() > o2.getPrice()){
            return 1;
        }else if (o1.getPrice() < o2.getPrice()){
            return -1;
        }else return 0;
    }
}

之前使用 Comparable 接口的时候直接使用的 Arrays.sort()方法进行排序的,现在更换了 Comparator 之后,可以使用另一个重载的 sort 方法。

public static <T> void sort(T[] a, Comparator<? super T> c) {}

重新实现排序

class Main {
    public static void main(String[] args) {
        Book books[] = new Book[]{
                new Book("test1", 10),
                new Book("test2", 30),
                new Book("test3", 50),
                new Book("test4", 5)
        };
        Arrays.sort(books,new BookComparator());
        System.out.println(Arrays.toString(books));
    }
}

out:
[书名:test4,价格:5.0, 书名:test1,价格:10.0, 书名:test2,价格:30.0, 书名:test3,价格:50.0]

使用 Comparator 比较麻烦。尽量使用 Comparable。

面试题:解释 Comparable 和 Comparator 的区别

  • 如果对象数组要进行排序,那么必须设置排序规则,可以使用上述两个接口。
  • java.lang.Comparable 是在一个类定义的时候要实现 compareTo()。
  • java.util.Comparator 是在一个类定义之后,专门实现 compare()方法的工具类。