java比较器:java.lang.Comparable和java.util.Comparator

本文详细介绍了Java中对象排序的两种方式:Comparable接口的自然排序和Comparator接口的定制排序。Comparable用于实现对象的自然排序,需要重写compareTo方法。Comparator则允许临时定制排序规则,适用于不便修改原有类的情况。示例代码展示了如何在自定义类中实现Comparable接口进行多字段排序,以及使用匿名内部类和lambda表达式实现Comparator。

java比较器:java.lang.Comparable和java.util.Comparator

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;

/*
 *  一、说明:java中的对象,正常情况下,只能比较:==或者!=。不能使用>或<的
 *   但是在开发场景中,需要对多个对象进行排序,言外之意,需要比较对象的大小。
 *   如何实现?使用两个接口中的任何一个:Comparable 和 Comparator
 *
 *  二、Comparable接口的使用
 *
 * */
public class Compare {
    /*
     * Comparable接口的使用举例(String的比较实现了Comparable接口)
     * */
    /*
    注意:单元测试Junit,不要把注解放在public static void main中
    * */
    @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){
            /*编译阶段,public int compareTo(Object o),对于o,编译器认为是Object的引用o,
            是所有类的父类,父类引用不能直接赋值给子类,所以必须使用强制类型转换(向下转型)
            * */
            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<Goods> g2=Arrays.asList(g);
    Consumer<Goods> 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){
        /*编译阶段,public int compareTo(Object o),对于o,
        编译器认为是Object的引用o,
        是所有类的父类,父类引用不能直接赋值给子类,
        所以必须使用强制类型转换(向下转型)
        * */
        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<Goods> g2=Arrays.asList(g);
    Consumer<Goods> 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(){
    /*
    Comparator
    * */
    String[] arr=new String[]{"CC","EE","AA","DD"};
    Arrays.sort(arr, new Comparator<String>() {
        @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(){
    /*
    Comparator
    * */
    String[] arr=new String[]{"CC","EE","AA","DD"};
    Comparator<String> 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<Goods>() {
        @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<Goods> g4=Arrays.asList(g);
    Consumer<Goods> 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接口属于临时性的比较。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值