Comparable和Comparator以及Arrays.sort方法

本文详细解析了Java中的Comparable与Comparator接口的区别、使用场景及策略模式的应用,阐述了它们在实现集合排序时的功能差异。通过具体示例展示了如何在不同情况下灵活运用这两种接口,以达到高效排序的目的。
 Comparable & Comparator 都是用来实现集合中元素的比较、排序的,只是 Comparable 是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序,所以,如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法。

Comparator位于包java.util下,而Comparable位于包   java.lang下

Comparable 是一个对象本身就已经支持自比较所需要实现的接口(如 String、Integer 自己就可以完成比较大小操作,已经实现了Comparable接口)   

 自定义的类要在加入list容器中后能够排序,可以实现Comparable接口,在用Collections类的sort方法排序时,如果不指定Comparator,那么就以自然顺序排序,如API所说:
Sorts the specified list into ascending order, according to the natural ordering of its elements. All elements in the list must implement the Comparable interface
这里的自然顺序就是实现Comparable接口设定的排序方式。

而 Comparator 是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。
  
可以说一个是自已完成比较,一个是外部程序实现比较的差别而已。

用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。
  

比如:你想对整数采用绝对值大小来排序,Integer 是不符合要求的,你不需要去修改 Integer 类(实际上你也不能这么做)去改变它的排序行为,只要使用一个实现了 Comparator 接口的对象来实现控制它的排序就行了。



Collections.sort((List<T> list, Comparator<? super T> c)是用来对list排序的。


如果不是调用sort方法,相要直接比较两个对象的大小,如下:
Comparator定义了俩个方法,分别是   int   compare(T   o1,   T   o2)和   boolean   equals(Object   obj),
用于比较两个Comparator是否相等
true only if the specified object is also a comparator and it imposes the same ordering as this comparator.
有时在实现Comparator接口时,并没有实现equals方法,可程序并没有报错,原因是实现该接口的类也是Object类的子类,而Object类已经实现了equals方法

 Comparable接口只提供了   int   compareTo(T   o)方法,也就是说假如我定义了一个Person类,这个类实现了   Comparable接口,那么当我实例化Person类的person1后,我想比较person1和一个现有的Person对象person2的大小时,我就可以这样来调用:person1.comparTo(person2),通过返回值就可以判断了;而此时如果你定义了一个   PersonComparator(实现了Comparator接口)的话,那你就可以这样:PersonComparator   comparator=   new   PersonComparator();
comparator.compare(person1,person2);。


两种方法各有优劣, 用Comparable 简单, 只要实现Comparable 接口的对象直接就成为一个可以比较的对象,但是需要修改源代码, 用Comparator 的好处是不需要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去就可以比大小了, 并且在Comparator 里面用户可以自己实现复杂的可以通用的逻辑,使其可以匹配一些比较简单的对象,那样就可以节省很多重复劳动了。

Comparable

Comparable 定义在 Person类的内部:

public class Persion implements Comparable {..比较Person的大小..},

 因为已经实现了比较器,那么我们的Person现在是一个可以比较大小的对象了,它的比较功能和String完全一样,可以随时随地的拿来
比较大小,因为Person现在自身就是有大小之分的。Collections.sort(personList)可以得到正确的结果。

Comparator

Comparator 是定义在Person的外部的, 此时我们的Person类的结构不需要有任何变化,如

public class Person{ String name; int age },

然后我们另外定义一个比较器:

public PersonComparator implements Comparator() {..比较Person的大小..},

在PersonComparator里面实现了怎么比较两个Person的大小. 所以,用这种方法,当我们要对一个 personList进行排序的时候, 
我们除了了要传递personList过去, 还需要把PersonComparator传递过去,因为怎么比较Person的大小是在PersonComparator
里面实现的, 如:

Collections.sort( personList , new PersonComparator() ).


Arrays.sort(T[], Comparator < ? super T > c) 方法用于对象数组按用户自定义规则排序.

使用策略模式
这是策略模式(Strategy pattern)的一个完美又简洁的示例,值得一提的是为什么这种场景下适合使用策略模式.
总体来说,策略模式允许在程序执行时选择不同的算法.比如在排序时,传入不同的比较器(Comparator),就采用不同的算法.
根据上面的例子,假设你想要根据Dog的重量来进行排序,可以像下面这样,创建一个新的比较器来进行排序:

[java]  view plain copy
  1. class Dog{  
  2.     int size;  
  3.     int weight;  
  4.    
  5.     public Dog(int s, int w){  
  6.         size = s;  
  7.         weight = w;   
  8.     }  
  9. }  
  10.    
  11. class DogSizeComparator implements Comparator<Dog>{  
  12.    
  13.     @Override  
  14.     public int compare(Dog o1, Dog o2) {  
  15.         return o1.size - o2.size;  
  16.     }  
  17. }  
  18.    
  19. class DogWeightComparator implements Comparator<Dog>{  
  20.    
  21.     @Override  
  22.     public int compare(Dog o1, Dog o2) {  
  23.         return o1.weight - o2.weight;  
  24.     }  
  25. }  
  26.    
  27. public class ArraySort {  
  28.    
  29.     public static void main(String[] args) {  
  30.         Dog d1 = new Dog(250);  
  31.         Dog d2 = new Dog(130);  
  32.         Dog d3 = new Dog(340);  
  33.    
  34.         Dog[] dogArray = {d1, d2, d3};  
  35.         printDogs(dogArray);  
  36.    
  37.         Arrays.sort(dogArray, new DogSizeComparator());   
  38.         printDogs(dogArray);  
  39.    
  40.         Arrays.sort(dogArray, new DogWeightComparator());     
  41.         printDogs(dogArray);  
  42.     }  
  43.    
  44.     public static void printDogs(Dog[] dogs){  
  45.         for(Dog d: dogs)  
  46.             System.out.print("size="+d.size + " weight=" + d.weight + " ");  
  47.    
  48.         System.out.println();  
  49.     }  
  50. }  
执行结果:
[plain]  view plain copy
  1. size=2 weight=50 size=1 weight=30 size=3 weight=40  
  2. size=1 weight=30 size=2 weight=50 size=3 weight=40  
  3. size=1 weight=30 size=3 weight=40 size=2 weight=50  
Comparator 是一个接口,所以sort方法中可以传入任意实现了此接口的类的实例,这就是策略模式的主要思想.

3. 为何使用"super"
如果使用 "Comparator < T > c" 那是很简单易懂的,但是sort的第2个参数里面的 < ? super T > 意味着比较器所接受的类型可以是T或者它的超类. 为什么是超类呢? 答案是: 这允许使用同一个比较器对不同的子类对象进行比较.在下面的示例中很明显地演示了这一点:
[java]  view plain copy
  1. import java.util.Arrays;  
  2. import java.util.Comparator;  
  3.    
  4. class Animal{  
  5.     int size;  
  6. }  
  7.    
  8. class Dog extends Animal{  
  9.     public Dog(int s){  
  10.         size = s;  
  11.     }  
  12. }  
  13.    
  14. class Cat extends Animal{  
  15.     public Cat(int s){  
  16.         size  = s;  
  17.     }  
  18. }  
  19.    
  20. class AnimalSizeComparator implements Comparator<Animal>{  
  21.    
  22.     @Override  
  23.     public int compare(Animal o1, Animal o2) {  
  24.         return o1.size - o2.size;  
  25.     }  
  26.     //in this way, all sub classes of Animal can use this comparator.  
  27. }  
  28.    
  29. public class ArraySort {  
  30.    
  31.     public static void main(String[] args) {  
  32.         Dog d1 = new Dog(2);  
  33.         Dog d2 = new Dog(1);  
  34.         Dog d3 = new Dog(3);  
  35.    
  36.         Dog[] dogArray = {d1, d2, d3};  
  37.         printDogs(dogArray);  
  38.    
  39.         Arrays.sort(dogArray, new AnimalSizeComparator());    
  40.         printDogs(dogArray);  
  41.    
  42.         System.out.println();  
  43.    
  44.         //when you have an array of Cat, same Comparator can be used.   
  45.         Cat c1 = new Cat(2);  
  46.         Cat c2 = new Cat(1);  
  47.         Cat c3 = new Cat(3);  
  48.    
  49.         Cat[] catArray = {c1, c2, c3};  
  50.         printDogs(catArray);  
  51.    
  52.         Arrays.sort(catArray, new AnimalSizeComparator());    
  53.         printDogs(catArray);  
  54.     }  
  55.    
  56.     public static void printDogs(Animal[] animals){  
  57.         for(Animal a: animals)  
  58.             System.out.print("size="+a.size + " ");  
  59.    
  60.         System.out.println();  
  61.     }  
  62. }  
输出结果:
[plain]  view plain copy
  1. size=2 size=1 size=3  
  2. size=1 size=2 size=3  
  3. size=2 size=1 size=3  
  4. size=1 size=2 size=3  

4. 小结
与Arrays.sort()相关的信息总结如下:
  1. 通用: super 类
  2. 策略设计模式(strategy pattern);
  3. 归并排序(merge sort): 时间复杂度 n*log(n);
  4. Java.util.Collections#sort(List < T > list, Comparator < ? super T > c)与Arrays.sort 使用类似的思想.

<think>我们正在讨论Java中的Arrays.sort方法。根据用户需求,需要介绍其使用方法原理,并参考提供的引用内容。 引用内容摘要: [^1]: 介绍了Arrays.sort()方法的使用场景、定义等,但标题是"Java实战入门:深入解析Java中的 `Arrays.sort()` 方法",说明它可能是关于Arrays.sort的详细解析。 [^2]: 提到Arrays.sort()Collections.sort()的底层原理,指出基本类型数组对象数组的排序方式不同,基本类型用快速排序的变体,对象用归并排序(但注意,实际在Java中,对象数组排序在早期版本是归并,后来改为TimSort)。 [^3]: 明确说明不论是Collections.sort方法或者是Arrays.sort方法,底层实现都是TimSort实现的(这是jdk1.7新增的),以前是归并排序。并解释了TimSort算法的基本思想:找到已经排好序数据的子序列(run),然后对剩余部分排序,再合并。 [^4]: 提到Arrays.sort()对快速排序的优化,特别是处理相等元素的优化(使得相等的元素能够放在附近,从而减少不必要的交换)。 [^5]: 列举了Collections类中的其他方法(如混排、反转等),但并没有直接关于Arrays.sort原理的更多信息。 因此,我们可以组织答案如下: 1. 方法定义使用场景: 根据[^1],Arrays.sort()用于对数组排序,支持基本数据类型数组对象数组。 2. 使用方法: 基本数据类型数组:直接使用Arrays.sort(数组) 对象数组:需要对象实现Comparable接口,或者提供Comparator 3. 实现原理(重点): 根据[^2][^3][^4]: - 对于基本数据类型数组:使用双轴快速排序(Dual-Pivot Quicksort),这是对经典快速排序的优化,由Vladimir Yaroslavskiy在2009年提出。它使用两个基准元素将数组分成三部分,然后递归排序。这种算法在大多数情况下比传统单轴快速排序更快,特别是对于大量数据。同时,它也对连续相等元素做了优化(避免不必要的交换)[^4]。 - 对于对象数组:使用TimSort算法(从Java 7开始)。TimSort是一种归并排序插入排序的混合排序算法,它利用了数据中已存在的有序片段(称为run),然后合并这些run。TimSort是稳定的排序算法(即相等元素的相对顺序不变)。 4. 示例代码: 分别展示基本类型数组对象数组的排序。 5. 注意事项: - 对象数组排序时,如果没有实现Comparable接口,则必须在调用时提供Comparator,否则会抛出ClassCastException。 - 排序会直接修改原数组(原地排序)。 - 对于基本类型数组,排序算法是非稳定的,但对于对象数组,排序是稳定的(因为使用TimSort)。 6. 与Collections.sort()的关系: 根据[^2][^3],Collections.sort()底层也是调用Arrays.sort(),但是它是针对List接口的,内部会先将列表转换为数组,然后使用Arrays.sort()排序,最后将排序后的数组复制回列表。 7. 性能分析: - 时间复杂度:平均为$O(n \log n)$,最坏情况下,快速排序为$O(n^2)$,但双轴快速排序在实现中通过多种技巧(如小数组用插入排序、选择好的基准等)减少了最坏情况发生的概率。TimSort的最坏时间复杂度也是$O(n \log n)$,最好情况(已经有序)是$O(n)$。 - 空间复杂度:双轴快速排序是$O(\log n)$(递归栈),TimSort是$O(n)$(因为归并排序需要额外的空间)。 根据以上思路,我们组织答案。 </think>### Java中`Arrays.sort()`方法解析 `Arrays.sort()`是Java中用于数组排序的核心方法,支持基本类型数组对象数组的高效排序。以下从使用方法底层原理两方面深入解析。 --- #### 一、使用方法 ##### 1. **基本类型数组排序** 直接调用`Arrays.sort()`,默认升序排序: ```java int[] numbers = {5, 2, 9, 1}; Arrays.sort(numbers); // 原地排序,结果为[1, 2, 5, 9] ``` - **支持类型**:`int[]`、`double[]`、`char[]`等基本类型数组[^1]。 ##### 2. **对象数组排序** 对象数组需满足以下条件之一: - **实现`Comparable`接口**: ```java class Person implements Comparable<Person> { String name; int age; // 实现compareTo方法定义自然顺序 @Override public int compareTo(Person other) { return Integer.compare(this.age, other.age); } } Person[] people = new Person[]{...}; Arrays.sort(people); // 按年龄升序 ``` - **提供`Comparator`**: ```java Arrays.sort(people, Comparator.comparing(Person::getName)); // 按姓名排序 Arrays.sort(people, Comparator.comparingInt(Person::getAge).reversed() // 按年龄降序 ); ``` ⚠️ **未实现`Comparable`且未提供`Comparator`会抛出`ClassCastException`**[^1][^2]。 ##### 3. **部分排序** 对数组指定区间排序: ```java int[] arr = {7, 3, 8, 2, 5}; Arrays.sort(arr, 1, 4); // 对索引[1,4)排序 → [7, 2, 3, 8, 5] ``` --- #### 二、底层原理 根据数组类型选择不同算法: ##### 1. **基本类型数组:双轴快速排序(Dual-Pivot Quicksort)** - **核心思想**:使用两个基准值(pivot)将数组分为三部分: $$ \text{左侧} < \text{Pivot1} \leq \text{中间} < \text{Pivot2} \leq \text{右侧} $$ 递归处理子数组[^2][^4]。 - **优化点**: - 小数组(长度<47)转插入排序减少递归开销 - 处理连续相等元素时减少交换次数[^4] - **时间复杂度**: - 平均:$O(n \log n)$ - 最坏:$O(n^2)$(概率极低) ##### 2. **对象数组:TimSort算法** - **核心思想**:归并排序 + 插入排序优化[^3]: 1. **寻找有序子序列(Run)**:扫描数组识别升序/降序片段 2. **插入排序优化**:短序列(长度<32)用二分插入排序 3. **归并合并**:按规则合并相邻有序子序列 - **优势**: - 稳定排序(相等元素顺序不变) - 对部分有序数据效率接近$O(n)$[^3] - **时间复杂度**:最坏$O(n \log n)$ --- #### 三、关键注意事项 1. **原地排序**:直接修改原数组,无返回值[^1][^2]。 2. **稳定性差异**: - 对象数组排序**稳定**(TimSort特性) - 基本类型数组排序**非稳定** 3. **性能对比**: | 场景 | 推荐方法 | 原因 | |-------------------|-------------------|--------------------------| | 基本类型大数组 | `Arrays.sort()` | 双轴快排缓存命中率高 | | 对象数组/链式排序 | `Collections.sort()` | 底层调用`Arrays.sort()` | [^2][^5] --- #### 四、示例场景 ```java // 混合排序:先按姓名升序,再按年龄降序 Arrays.sort(people, Comparator.comparing(Person::getName) .thenComparing(Comparator.comparingInt(Person::getAge).reversed()) ); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值