• Java - 浅析 Comparable 和 Comparator


    概述

    Java 中我们知道基本数据类型,比如 intfloat 等可以使用 ==!=>< 来进行大小比较,但是对象或者包装类之间怎么进行比较呢,Java 也给我们提供了两个相关的类:ComparableComparator

    public interface Comparable<T> {}
    
    • 1
    @FunctionalInterface
    public interface Comparator<T> {}
    
    • 1
    • 2

    那么它们两个有什么区别呢:

    • 从命名上

      从命名上,Comparable 是形容词,Comparator 是名词。可以理解为实现了 Comparable 接口的类具备了相互之间比较的能力;而 Comparator 的接口其中定义了一些默认的比较规则,还可以实现这个接口来自定义比较规则

    • 从源码上

      从源码上,Comparable 是一个接口,实现这个接口的类要重写其中定义的 compareTo() 方法;Comparator 是一个函数式接口,可以通过 lambda 表达式来使用

    PriorityQueue

    PriorityQueue 是一个优先级队列,其中的元素会按照它们的自然顺序排序(实现 Comparable 接口),或者由队列构造时提供的 Comparator 排序,两者必须存在一个。我们来看一下 PriorityQueue 的源码:

    ublic class PriorityQueue<E> extends AbstractQueue<E>
        implements java.io.Serializable {
    
    	// 自定义 Comparator,由构造方法传入
    	private final Comparator<? super E> comparator;
    
    	public PriorityQueue() {
            this(DEFAULT_INITIAL_CAPACITY, null);
        }
    
    	public PriorityQueue(Comparator<? super E> comparator) {
            this(DEFAULT_INITIAL_CAPACITY, comparator);
        }
    	
    	public PriorityQueue(int initialCapacity,
                             Comparator<? super E> comparator) {
            // Note: This restriction of at least one is not actually needed,
            // but continues for 1.5 compatibility
            if (initialCapacity < 1)
                throw new IllegalArgumentException();
            this.queue = new Object[initialCapacity];
            this.comparator = comparator;
        }
    
    	public boolean add(E e) {
            return offer(e);
        }
    
    	public boolean offer(E e) {
            if (e == null)
                throw new NullPointerException();
            modCount++;
            int i = size;
            if (i >= queue.length)
                grow(i + 1);
            size = i + 1;
            if (i == 0)
                queue[0] = e;
            else
            	// 添加的时候排序
                siftUp(i, e);
            return true;
        }
    
    	private void siftUp(int k, E x) {
            if (comparator != null)
            	// 构造方法传入的 Comparator
                siftUpUsingComparator(k, x);
            else
            	// 使用类自身的 Comparable 能力
                siftUpComparable(k, x);
        }
    
    	private void siftUpComparable(int k, E x) {
    		// 如果类没有实现 Comparable 接口,这里会抛出 ClassCastException 类型转化异常
            Comparable<? super E> key = (Comparable<? super E>) x;
            while (k > 0) {
                int parent = (k - 1) >>> 1;
                Object e = queue[parent];
                if (key.compareTo((E) e) >= 0)
                	// 如果要添加进来的值,大于等于 e,则不做任何操作
                    break;
                queue[k] = e;
                k = parent;
            }
            queue[k] = key;
        }
    
    	private void siftUpUsingComparator(int k, E x) {
            while (k > 0) {
                int parent = (k - 1) >>> 1;
                Object e = queue[parent];
                if (comparator.compare(x, (E) e) >= 0)
                	// 如果
                    break;
                queue[k] = e;
                k = parent;
            }
            queue[k] = x;
        }
    }
    
    • 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

    接下来就通过 PriorityQueue 来理解 ComparableComparator 这两个概念。

    Comparable

    要理解 Comparable,我们首先来看一下 JavaInteger 类的源码:

    public final class Integer extends Number implements Comparable<Integer> {
    	...
    	
    	public int compareTo(Integer anotherInteger) {
            return compare(this.value, anotherInteger.value);
        }
    
    	public static int compare(int x, int y) {
            return (x < y) ? -1 : ((x == y) ? 0 : 1);
        }
        
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以看到 Integer 类实现了 Comparable 接口,并且重写了 compareTo() 方法,说明 Integer 类具有了比较的能力。compareTo() 方法返回值是一个 int 类型的数字,为 -1 表示传入的对象大,为 0 表示一样大,1 表示 this 对象大。

    使用 PriorityQueue 测试 IntegerComparable 的测试用例:

    void testComparable() {
    	// Integer 实现了 Comparable,所以这里可以使用无参构造器
       	Queue<Integer> queue = new PriorityQueue<>();
        queue.add(76);
        queue.add(1);
        queue.add(8);
        queue.add(4);
        queue.add(88);
        queue.add(54);
    
        System.out.println(queue.element());	// 1
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Comparator

    首先定义一个 People 类,只有一个年龄 age 属性:

    @Data
    @AllArgsConstructor
    public class People {
    
        private Integer age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    定义一个 Comparator

    public class MyComparator implements Comparator<People> {
        @Override
        public int compare(People o1, People o2) {
        	// 返回正整数表示 o1 > o2
            return o1.getAge() - o2.getAge();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试用例:

    void testComparator() {
    	// 这里一定要传入自定义的 Comparator
    	Queue<People> queue = new PriorityQueue<>(new MyComparator());
        queue.add(new People(21));
        queue.add(new People(10));
        queue.add(new People(44));
        queue.add(new People(56));
        queue.add(new People(35));	// People(age=10)
    
        System.out.println(queue.element());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    总结

    对于 ComparableComparator 的应用还是非常简单的,从上面两个示例中基本就能理解,所以就不做过多的解释了,无非是要分清楚谁大谁小而已:

    • 对于实现 Comparable 接口的 compareTo() 方法来说,返回值为正表示传入的对象大,为负表示传入的对象小于 this 对象。

    • 对于实现 Comparator 接口的 compare() 方法来说,返回值为正表示传入的第一个对象大于第二个对象,为负表示第一个对象小于第二个对象。

  • 相关阅读:
    鸿蒙APP开发时WebView导致Log功能异常
    【Spatial-Temporal Action Localization(四)】论文阅读2019年
    iphone14来了,可是约好的你去哪了
    Hyper-V 简介
    【文献分享】NASA JPL团队CoSTAR一大力作:直接激光雷达里程计:利用密集点云快速定位
    鸿蒙 Harmony ArkTs开发教程三 流程控制
    重新认识架构—不只是软件设计
    开源模型应用落地-LangChain高阶-知识图谱助力记忆增强
    ATFX汇市:非美货币扎堆升值,唯有USDCAD表现平平
    JVM与Java体系结构
  • 原文地址:https://blog.csdn.net/qiaohao0206/article/details/126374229