栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java比较器:java.lang.Comparable和java.util.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;


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接口属于临时性的比较。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/298791.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号