1 介绍
在java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题。
java实现对象排序的方式有两种:
自然排序:java.lang.Comparable
定制排序:java.util.Comparator
2 java.lang.Comparable
2.1 方式一:自然排序,java.lang.Comparable
Comparable接口强行对实现它的每个类的对象进行整体排序,这种排序被称为类的自然排序。
实现Comparable的类必须实现compareTo(Object obj)方法,两个对象即通过compareTo(Object obj)方法的返回值来比较大小。
实现Comparable接口的对象列表(和数组)可以通过Collections.sort或Arrays.sort进行自动排序。实现此接口的对象可用作有序映射中的键或有序集合中的元素,无需指定比较器。
对于类C的每一个e1和e2来说,当且仅当e1.compareTo(e2)==0与e1.equals(e2)具有相同的boolean值时,类C的自然排序才叫做与equals一致。建议(但不是必须)最好使自然排序与equals一致。
2.2 实例
注意:String类实现了Comparable接口,重写了Comparable接口的compareTo方法,所以可以进行字符串比较,即String的compareTo方法。
import org.junit.Test;
import java.util.Arrays;
public class Compare {
@Test
public void test1(){
String[] arr=new String[]{"CC","EE","AA","DD"};
System.out.println(Arrays.asList(arr));
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
[CC, EE, AA, DD] [AA, CC, DD, EE]
注意:java.util.Comparator是JDK 1.8 之前已有的函数式接口
2.3 自定义类实现Comparable自然排序
源码可知,这三个类都实现了Comparable接口。
鼠标移到Goods类里面,idea快捷键生成getter和setter,以及constructor:alt+insert
商品类:
import java.math.BigDecimal;
import java.math.BigInteger;
public class Goods {
private String name;
private BigDecimal price;
private BigInteger number;
public Goods() {
}
public Goods(String name, BigDecimal price, BigInteger number) {
this.name = name;
this.price = price;
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public BigInteger getNumber() {
return number;
}
public void setNumber(BigInteger number) {
this.number = number;
}
}
@Test
public void test2(){
Goods[] g=new Goods[4];
g[0]=new Goods("xiaomiMouse",new BigDecimal("3.45"),new BigInteger("3"));
g[1]=new Goods("huaweiMouse",new BigDecimal("1.35"),new BigInteger("10"));
g[2]=new Goods("dellMouse",new BigDecimal("8.99"),new BigInteger("7"));
g[3]=new Goods("lenovoMouse",new BigDecimal("4.5"),new BigInteger("14"));
Arrays.sort(g);
System.out.println(Arrays.toString(g));
}
comparable接口:自然排序
自定义类实现Comparable接口:
import java.math.BigDecimal;
import java.math.BigInteger;
public class Goods implements Comparable{
private String name;
private BigDecimal price;
private BigInteger number;
public Goods() {
}
public Goods(String name, BigDecimal price, BigInteger number) {
this.name = name;
this.price = price;
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public BigInteger getNumber() {
return number;
}
public void setNumber(BigInteger number) {
this.number = number;
}
//指明商品比较大小的方式
@Override
public int compareTo(Object o) {
//判断o是不是Goods类的实例或者子类
if(o instanceof Goods){
Goods goods= (Goods) o;
if(this.price.compareTo(goods.price) > 0){
return 1;
}else if(this.price.compareTo(goods.price) < 0){
return -1;
}else{
return 0;
}
}
}
}
方式一:
方式二:
可以使用Integer.compare或者Double.compare等
因为该方法返回值类型是int,但是return 0没有意义,如果不是Goods类型,直接抛出异常即可。
重新执行test2:
[Goods@17d10166, Goods@1b9e1916, Goods@ba8a1dc, Goods@4f8e5cde]
数组的打印,是打印的对象的地址,第一种方式是通过重写打印的对象的类的toString方法来修改打印的对象,第二种方式就是Arrays.asList(数组)转换成集合,通过集合对象.forEach()或者集合对象.stream().forEach()来打印。
查看源码,Arrays.toString()本质上是循环对数组元素调用String.valueOf(),String.valueOf()源码如下:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
每个obj对象不为null,则调用obj.toString(),obj.toString()如下所示:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
需要重写类的中toString()方法,同样还是idea在类中alt+insert,选择toString():
@Override
public String toString() {
return "Goods{" +
"name='" + name + ''' +
", price=" + price +
", number=" + number +
'}';
}
第一种方式打印数组:Arrays.toString(对象数组)按照自然排序由小到大的方式):
@Test
public void test2(){
Goods[] g=new Goods[4];
g[0]=new Goods("xiaomiMouse",new BigDecimal("3.45"),new BigInteger("3"));
g[1]=new Goods("huaweiMouse",new BigDecimal("1.35"),new BigInteger("10"));
g[2]=new Goods("dellMouse",new BigDecimal("8.99"),new BigInteger("7"));
g[3]=new Goods("lenovoMouse",new BigDecimal("4.5"),new BigInteger("14"));
Arrays.sort(g);
System.out.println(Arrays.toString(g));
}
//打印出来实际是一行字符串(这里方便显示,手动换行)
[Goods{name='huaweiMouse', price=1.35, number=10},
Goods{name='xiaomiMouse', price=3.45, number=3},
Goods{name='lenovoMouse', price=4.5, number=14},
Goods{name='dellMouse', price=8.99, number=7}]
第2种方式每个对象打印(打印的效果还是按照类中重写的toString()方法而来):
@Test
public void test2(){
Goods[] g=new Goods[4];
g[0]=new Goods("xiaomiMouse",new BigDecimal("3.45"),new BigInteger("3"));
g[1]=new Goods("huaweiMouse",new BigDecimal("1.35"),new BigInteger("10"));
g[2]=new Goods("dellMouse",new BigDecimal("8.99"),new BigInteger("7"));
g[3]=new Goods("lenovoMouse",new BigDecimal("4.5"),new BigInteger("14"));
Arrays.sort(g);
List g2=Arrays.asList(g);
Consumer s= System.out::println;
g2.forEach(s);
}
Goods{name='huaweiMouse', price=1.35, number=10}
Goods{name='xiaomiMouse', price=3.45, number=3}
Goods{name='lenovoMouse', price=4.5, number=14}
Goods{name='dellMouse', price=8.99, number=7}
2.4 自定义类,实现Comparable,按照多个要求进行排序
//指明商品比较大小的方式
//先按照价格由低到高排序,再按照数目由低到高排列
@Override
public int compareTo(Object o) {
//判断o是不是Goods类的实例或者子类
if(o instanceof Goods){
Goods goods= (Goods) o;
if(this.price.compareTo(goods.price) > 0){
return 1;
}else if(this.price.compareTo(goods.price) < 0){
return -1;
}else{
return this.number.compareTo(goods.number);
}
}
throw new RuntimeException("传入的数据类型不一致!");
}
@Test
public void test2(){
Goods[] g=new Goods[4];
g[0]=new Goods("xiaomiMouse",new BigDecimal("3.45"),new BigInteger("3"));
g[1]=new Goods("huaweiMouse",new BigDecimal("1.35"),new BigInteger("10"));
g[2]=new Goods("dellMouse",new BigDecimal("8.99"),new BigInteger("7"));
g[3]=new Goods("lenovoMouse",new BigDecimal("8.99"),new BigInteger("4"));
Arrays.sort(g);
List g2=Arrays.asList(g);
Consumer s= System.out::println;
g2.forEach(s);
}
//先按照价格,再按照数目,都是由小到大
Goods{name='huaweiMouse', price=1.35, number=10}
Goods{name='xiaomiMouse', price=3.45, number=3}
Goods{name='lenovoMouse', price=8.99, number=4}
Goods{name='dellMouse', price=8.99, number=7}
如果要数目从大到小排序,如下修改:
再次执行如下:
3 java.util.Comparator
3.1 方式2:定制排序,java.util.Comparator
当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用Comparator的对象来排序,强行对多个对象进行整体排序的比较。
重写**compare(Object o1,Object o2)**方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
可以将Comparator传递给sort方法(如Collections.sort或Arrays.sort),从而允许在排序顺序上实现精确控制。
还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。
tips:idea搜索类名(找到这个类所在的源码位置),ctrl+shift+A:
函数式接口只有一个抽象方法:compare
3.2 匿名内部类重写Comparator接口的compare方法
@Test
public void test3(){
String[] arr=new String[]{"CC","EE","AA","DD"};
Arrays.sort(arr, new Comparator() {
@Override
public int compare(String o1, String o2) {
return -o1.compareTo(o2);
}
});
System.out.println(Arrays.toString(arr));
}
[EE, DD, CC, AA]
3.3 java8的新特性lambda表达式重写compare方法
@Test
public void test3(){
String[] arr=new String[]{"CC","EE","AA","DD"};
Comparator u=(k1,k2)->-k1.compareTo(k2);
Arrays.sort(arr, u);
System.out.println(Arrays.toString(arr));
}
[EE, DD, CC, AA]
3.4 自定义类使用Comparator
@Test
public void test4(){
Goods[] g=new Goods[4];
g[0]=new Goods("xiaomiMouse",new BigDecimal("3.45"),new BigInteger("3"));
g[1]=new Goods("huaweiMouse",new BigDecimal("1.35"),new BigInteger("10"));
g[2]=new Goods("dellMouse",new BigDecimal("8.99"),new BigInteger("7"));
g[3]=new Goods("lenovoMouse",new BigDecimal("8.99"),new BigInteger("4"));
Arrays.sort(g, new Comparator() {
@Override
public int compare(Goods o1, Goods o2) {
if(o1.getPrice().equals(o2.getPrice())){
return -o1.getNumber().compareTo(o2.getNumber());
}else{
return o1.getPrice().compareTo(o2.getPrice());
}
}
});
List g4=Arrays.asList(g);
Consumer goo=System.out::println;
g4.forEach(goo);
}
Goods{name='huaweiMouse', price=1.35, number=10}
Goods{name='xiaomiMouse', price=3.45, number=3}
Goods{name='dellMouse', price=8.99, number=7}
Goods{name='lenovoMouse', price=8.99, number=4}
4 Comparable和Comparator使用的对比
Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
Comparator接口属于临时性的比较。



