[TOC]

一. 比较器 (Arrays类)

     在之前一直使用 “java.util.Arrays.sort()” 可以实现数组的排序,而Arrays类就是java.util包中提供的一个工具类,这个工具类主要是完成数组有关的操作功能。
     在这个类里面存在二分查找法:
public static int binarySearch(byte[] a,byte key)
public static int binarySearch(数据类型[] a, 数据类型 key)

1
2
3
4
5
6
7
8
9
package cn.ngp.demo;
import java.util.Arrays;
public class TestDemo {
public static void main(String[] args) {
int[] data = new int[] {1,5,6,2,3,4,9,8,7,10};
java.util.Arrays.sort(data);// 排序
System.out.println(Arrays.binarySearch(data, 9));
}
}

     Arrays类提供了数据比较: public static boolean equals(数据类型 [] a,数据类型[] a2),只不过借用了equals()方法名称,与Object类的equals()屁关系都没有。

1
2
3
4
5
6
7
8
9
package cn.ngp.demo;
import java.util.Arrays;
public class TestDemo {
public static void main(String[] args) {
int[] dataA = new int[] {1,2,3};
int[] dataB = new int[] {1,2,3};
System.out.println(Arrays.equals(dataA, dataB));
}
}

     要判断数组是否相同,需要顺序完全一致。

其他操作:

  • 填充数组: static void fill(数据类型[] a, 数据类型 val);
  • 将数组变为字符串输出: public static String toString(数据类型 [] a);
1
2
3
4
5
6
7
8
9
package cn.ngp.demo;
import java.util.Arrays;
public class TestDemo {
public static void main(String[] args) {
int[] data = new int[10];
Arrays.fill(data, 3);
System.out.println(Arrays.toString(data));
}
}

     以上实际上就属于数组的基本操作,只不过这样的操作在实际的开发里面很少出现。

二. 比较器 Comparable(核心)

     下面来观察一下Arrays类中提供的数组排序方法:

  • 对象数组排序: public static void sort(Object[] a);
         发现Arrays类里面可以直接利用sort()方法实现对象数组排序。

范例(错误):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package cn.ngp.demo;
import java.util.Arrays;
class Book {
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
@Override
public String toString() {
return "书名:《" + this.title + "》,价格:" + this.price + "元。" + "\n";
}
}
public class TestDemo {
public static void main(String[] args) {
Book books[] = new Book[] {
new Book("Java从入门到精通", 79.6),
new Book("Oracle从入门到精通", 88.6),
new Book("Android从入门到精通", 66.9) };
Arrays.sort(books);
System.out.println(Arrays.toString(books));
}
}

Exception in thread “main” java.lang.ClassCastException: cn.ngp.demo.Book cannot be cast to java.lang.Comparable
at java.util.ComparableTimSort.countRunAndMakeAscending(Unknown Source)
at java.util.ComparableTimSort.sort(Unknown Source)
at java.util.Arrays.sort(Unknown Source)
at cn.ngp.demo.TestDemo.main(TestDemo.java:21)

     造成此类异常只有一个原因: 两个没有关系的类对象发生了强制性的转换。
     每一个对象实际上只保留有地址信息,地址里面是有内容的,所以如果是普通的int型数据要进行比较,只要判断大小就够了,但是如果是对象数组,里面包含的如果只是编码(地址)比较式没意义的 ,就拿上面的代码来讲,应该按照价格排序才是有意义的,所以此处必须明确设置出比较的规则:
     比较的规则就是由Comparable接口定义的,此接口定义如下:

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

     实际上String类就是Comparable接口的子类,之前使用的compareTo()方法就是比较的操作功能,如果用户现在针对于对象进行比较,建议compareTo()方法返回三类数据: 1(大于)、0(等于)、-1(小于)

范例:

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
26
27
28
29
30
31
32
33
34
package cn.ngp.demo;
import java.util.Arrays;
class Book implements Comparable<Book> {
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
@Override
public String toString() {
return "书名:《" + this.title + "》,价格:" + this.price + "元。" + "\n";
}
@Override
public int compareTo(Book o) {
if (this.price > o.price) {
return 1;
} else if (this.price < o.price) {
return -1;
} else {
return 0;
}
}
}
public class TestDemo {
public static void main(String[] args) {
Book books[] = new Book[] {
new Book("Java从入门到精通", 79.6),
new Book("Oracle从入门到精通", 88.6),
new Book("Android从入门到精通", 66.9) };
Arrays.sort(books);
System.out.println(Arrays.toString(books));
}
}

     compareTo()方法由Arrays.sort()自动进行调用。

总结:

  • 以后不管何种情况下,只要是一组对象要排序,对象所在的类一定要实现comparable接口。

三. 数据结构 —— BinaryTree(了解)

     树是一种比链表更为复杂的概念应用,本质也属于动态对象数组,但是与链表不同在于,树的最大特征是可以针对于数据进行排序。
     树的操作原理: 选择第一个数据作为根节点,而后比根节点小的放在根节点的左子树(左节点),比根节点大的数据放在右子书(右节点),取得的时候按照中序遍历的方式取出(左-中-右)。
     在任何数据结构里面Node类的核心功能是保存真实数据以及配置节点关系。

范例: 实现二叉树

  • 定义出要使用的数据,数据所在的类实现comparable接口
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package cn.ngp.demo;
import java.util.Arrays;
class Book implements Comparable<Book> {
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
@Override
public String toString() {
return "书名:《" + this.title + "》,价格:" + this.price + "元。" + "\n";
}
@Override
public int compareTo(Book o) {
if (this.price > o.price) {
return 1;
} else if (this.price < o.price) {//此处改为">"号,即变为升序输出
return -1;
} else {
return 0;
}
}
}
@SuppressWarnings("rawtypes")
class BinaryTree{
private class Node{
private Comparable data;//排序的依据就是Comparable
private Node left; //保存左节点
private Node right; //保存右节点
@SuppressWarnings("unused")
public Node(Comparable data) {
this.data = data;
}
@SuppressWarnings("unchecked")
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;//必须编程comparable才可以实现Node保存
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;
}
}
public class TestDemo {
public static void main(String[] args) {
BinaryTree bt = new BinaryTree();
bt.add(new Book("Java从入门到精通", 79.8));
bt.add(new Book("JSP从入门到精通", 68.8));
bt.add(new Book("Oracle从入门到精通", 98.8));
bt.add(new Book("Android从入门到精通", 89.8));
Object obj[] = bt.toArray();
System.out.println(Arrays.toString(obj));
}
}

     可是这些内容,Java的类库都有了自己的实现,所以这个代码是基本功的问题。
     自己对以上代码的理解: 用二叉树的结构实现了一种插入式的链表

四. 挽救的比较器 Comparator (理解)

     由于初期的设计并没有安排此类对象数组的排序。而后又突然需要实现对象数组的排序,那么这个时候,在不能修改Book类定义的情况下是不可能使用Comparable接口的,为此,在Java里面为了解决这个问题,又出现了另一个比较器: java.util.Comparator ,原本在Comparator接口下有两个方法:

1
2
3
4
5
@FunctionalInterface
public interface Comparator<T>{
int compare(T o1, T o2)
boolean equals(Object obj)
}

     而真正实现的只有compara()方法,需要单独准备出一个类来实现Comparator接口,这个类将作为指定类的排序类。

范例: 定义排序的工具类

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package cn.ngp.demo;
import java.util.Arrays;
import java.util.Comparator;
class Book {
private String title;
private double price;
public Book() {}
public Book(String title,double price) {
this.title = title;
this.price = price;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "书名: " + this.title + ", 价格: " + this.price + "\n";
}
}
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;
}
}
}
public class TestDemo {
public static void main(String[] args) {
Book books [] = new Book [] {
new Book("Java从入门到精通", 79.8),
new Book("JSP从入门到精通", 68.8),
new Book("Oracle从入门到精通", 98.8),
new Book("Android从入门到精通", 89.8)
};
//之前使用Comparable 接口的时候利用的是Arrays类中的sort()方法,
//可是现在更换了一个接口之后,那么也可以使用另外一个被重载的sort()方法:
//public static <T> void sort(T[] a,Comparator<? super T> c)
Arrays.sort(books,new BookComparator());
System.out.println(Arrays.toString(books));
}
}

     使用Comparator比较麻烦,因为要定义一个专门的排序类,而且调用排序的时候也要明确的指明一个排序规则类。
面试题: 请解释Comparable 和Comparator的区别?(面试题: 请解释两种比较器的区别)

  • 如果对象数组要进行排序那么必须设置排序规则,可以使用Compatable或Comparator接口实现;
  • java.lang.Comparable 是在一个类定义的时候实现好的接口,这样本类的对象的数组就可以进行排序,在Comparable接口下定义有一个CompareTo()方法
  • java.util.Comparator 是专门定义一个指定类的比较规则,属于挽救的比较操作,里面有两个方法: public int compare()、public boolean equals()。

总结:

  1. 以后不管何种情况下只要是牵扯到对象数组的排序一定使用Comparable接口;
  2. 根据自己的实际情况来决定是否要熟练编写链表

参考

来自: 阿里云大学(笔记) → 零基础学Java10系列三:Java高级编程