java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java中的排序

Java中的排序与内部比较器Compareable解析

作者:JFS_Study

这篇文章主要介绍了Java中的排序与内部比较器Compareable解析,一般没有特殊要求时,直接调用(底层默认的升序排列)就可以得到想要的结果,所谓的 sort 方法排序底层都是基于这两种排序,故如果需要设计成所想要的排序就需要了解底层排序原理,需要的朋友可以参考下

一、Arrays.sort() 与 Collections.sort() 排序

Arrays.sort() 可以对数组,字符串等排序

import java.util.Arrays;
 
public class sort {
  public static void main(String[] args) {
    int[] num = new int[]{3,2,4,1,5};
    Arrays.sort(num);
    for(int i=0;i<num.length;i++) {
      System.out.print(num[i]+" ");
    }
  }
}
//1 2 3 4 5

Collections.sort() 是对list集合排序,list 也可以放数字、字符串

import java.util.ArrayList;
import java.util.Collections;
 
public class sort {
  public static void main(String[] args) {
    ArrayList<Integer> list  = new ArrayList<>();
    list.add(3);
    list.add(2);
    list.add(1);
    Collections.sort(list);
    System.out.print(list);
  }
}
//[1, 2, 3]

一般没有特殊要求时,直接调用(底层默认的升序排列)就可以得到想要的结果。所谓的 sort 方法排序底层都是基于这两种排序,故如果需要设计成所想要的排序就需要了解底层排序原理。对自定义对象数组排序,需要引入“比较器”的概念。Compareable 和 Compartor 接口就是比较器抽象接口,通过实现类重写接口方法来进行对象比较。

二、内部比较器:Compareable 接口分析

当使用 sort(Objetc[] a) 来进行对象的自然排序时,该对象必需实现 Compareable 接口,重写 compareTo 方法,并一般在此方法中定义 3 种返回值(正,零,负)来进行排序标准的确认,一般默认下面值来表示:

应用:目标类实现 Comparable 接口,重写 compareTo(),定义排序规则,就可以直接调用 Collections.sort() 来排序对象数组。

@Data
public class Student implements Comparable{
    private int id;
    private int age;
    private int height;
    private String name;
    public Student(int id, String name, int age, int height) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.height = height;
    }
    @Override
    public int compareTo(Object o) {
        Student s = (Student) o;
        if (this.age > s.age) {
            return 1;
        }else if (this.age < s.age) {
            return -1;
        }else {
            if (this.height >= s.height) {
                return 1;
            }else {
                return -1;
            }
        }
    }
}

上面是实现升序排列,如果要实现降序只需要把 1 与 -1 互换位置即可。测试:

public class Test {
	public static void printData(List<Student> list) {
		for (Student student : list) {
			System.out.println("学号:"+student.getId()+"姓名:"+student.getName()+
"年龄"+student.getAge()+"身高:"+student.getHeight());
		}
	}
	public static void main(String[] args) {
		List<Student> list = new ArrayList<>();
		list.add(new Student(1, "A", 20, 180));
		list.add(new Student(2, "B", 21, 175));
		list.add(new Student(3, "C", 22, 190));
		list.add(new Student(4, "D", 21, 170));
		list.add(new Student(5, "E", 20, 185));
		System.out.println("before sorted");
		printData(list);
		Collections.sort(list);
		System.out.println("after age and height sorted");
		printData(list);
	}
}

结果:

before sorted
学号:1姓名:A年龄20身高:180
学号:2姓名:B年龄21身高:175
学号:3姓名:C年龄22身高:190
学号:4姓名:D年龄21身高:170
学号:5姓名:E年龄20身高:185
after age and height sorted
学号:1姓名:A年龄20身高:180
学号:5姓名:E年龄20身高:185
学号:4姓名:D年龄21身高:170
学号:2姓名:B年龄21身高:175
学号:3姓名:C年龄22身高:190

三、Compartor 接口分析

①先建一个基本属性类:

@Data
public class Student {
    //创建两个基本属性
    String name = "";
    int age = 0;
    //重写构造方法用来传递数据
    public Student(String name, int age) {
       super();
       this.name = name;
       this.age = age;
    }
}

创建按照姓名升序排列的实现类 :

import java.util.Comparator;
//按照名字的升序排列。实现接口,泛型是自定义类,里面有要排序的内容   
public class NameSort implements Comparator{
    @Override  
    //两个参数是泛型的对象
    public int compare(Student o1, Student o2) {
   //按照姓名的升序排列,前面加个负号就按照降序排列
       return o1.getName().compareTo(o2.getName());
    }
}

②直接定义Java中PriorityQueue优先级队列就是利用这原理:

//传进来的数组或是字符串以及集合list
Arrays.sort(num,new Comparator<String>(){
    @Override
    public int compare(String o1,String o2){
        return o1.compareTo(o2);//升
        //return o2.compareTo(o1);//降
    }
});

应用:实现比较器接口 Comparator,重写 compare 方法,直接当做参数传进 sort 中。

@Data
public class Student {
	private int id;
	private int age;
	private int height;
	private String name;
	public Student(int id, String name, int age, int height) {
		this.id = id;
		this.name = name;
		this.age = age;
		this.height = height;
	}
}

测试:

public class Test {
	public static void main(String[] args) {
		List<Student> list = new ArrayList<>();
		list.add(new Student(1, "A", 20, 180));
		list.add(new Student(2, "B", 21, 175));
		list.add(new Student(3, "C", 22, 190));
		list.add(new Student(4, "D", 21, 170));
		list.add(new Student(5, "E", 20, 185));
		System.out.println("before sorted");
		printData(list);
		Collections.sort(list, new Comparator<Student>() {
			@Override
			public int compare(Student o1, Student o2) {
				if(o1.getAge() >= o2.getAge()) {
					return 1;
				}
				else {
					return -1;
				}
			}
		});
		System.out.println("after age sorted");
		printData(list);
		Collections.sort(list, new Comparator<Student>() {
			@Override
			public int compare(Student o1, Student o2) {
				if(o1.getAge() > o2.getAge()) {
					return 1;
				}else if (o1.getAge() < o2.getAge()){
					return -1;
				}else {
					if (o1.getHeight() >= o2.getHeight()) {
						return 1;
					}else {
						return -1;
					}
				}
			}
		});
		System.out.println("after age and height sorted");
		printData(list);
	}
	public static void printData(List<Student> list) {
		for (Student student : list) {
			System.out.println("学号:"+student.getId()+"姓名:"+student.getName()+
"年龄"+student.getAge()+"身高:"+student.getHeight());
		}
	}
}

输出结果:

before sorted
学号:1姓名:A年龄20身高:180
学号:2姓名:B年龄21身高:175
学号:3姓名:C年龄22身高:190
学号:4姓名:D年龄21身高:170
学号:5姓名:E年龄20身高:185
after age sorted
学号:1姓名:A年龄20身高:180
学号:5姓名:E年龄20身高:185
学号:2姓名:B年龄21身高:175
学号:4姓名:D年龄21身高:170
学号:3姓名:C年龄22身高:190
after age and height sorted
学号:1姓名:A年龄20身高:180
学号:5姓名:E年龄20身高:185
学号:4姓名:D年龄21身高:170
学号:2姓名:B年龄21身高:175
学号:3姓名:C年龄22身高:190

单从上面的例子可以看出排序是稳定的,查看 Java 的Collections.sort的源代码,确实是基于稳定的归并排序实现的,内部还做了优化,叫TimSort。

API 排序

public static void main(String[] args) {

    List<Student> stuList = new ArrayList<>();
    stuList.add(new Student(1, "A", 20, 180));
    stuList.add(new Student(2, "B", 21, 175));
    stuList.add(new Student(3, "C", 22, 190));
    stuList.add(new Student(4, "D", 21, 170));
    stuList.add(new Student(5, "E", 20, 185));

    System.out.println(stuList);

    System.out.println("-------------先年龄升---后身高升-------------");
    Comparator<Student> compareAge = Comparator.comparing(Student::getAge);
    Comparator<Student> compareHight = Comparator.comparing(Student::getHeight);
    Collections.sort(stuList, compareAge.thenComparing(compareHight));
    for (Student s : stuList) {
        System.out.println(s.getName() + "," + s.getAge() + "," + s.getHeight());
    }

    System.out.println("-------------先年龄升---后身高降-------------");
    Collections.sort(stuList, compareAge.thenComparing(compareHight.reversed()));
    for (Student s : stuList) {
        System.out.println(s.getName() + "," + s.getAge() + "," + s.getHeight());
    }

    System.out.println("-------------先年龄降---后身高降-------------");
    Collections.sort(stuList, compareAge.reversed().thenComparing(compareHight.reversed()));
    for (Student s : stuList) {
        System.out.println(s.getName() + "," + s.getAge() + "," + s.getHeight());
    }
}

输出结果:

[Student(id=1, age=20, height=180, name=A), Student(id=2, age=21, height=175, name=B), Student(id=3, age=22, height=190, name=C), Student(id=4, age=21, height=170, name=D), Student(id=5, age=20, height=185, name=E)]
-------------先年龄升---后身高升-------------
A,20,180
E,20,185
D,21,170
B,21,175
C,22,190
-------------先年龄升---后身高降-------------
E,20,185
A,20,180
B,21,175
D,21,170
C,22,190
-------------先年龄降---后身高降-------------
C,22,190
B,21,175
D,21,170
E,20,185
A,20,180
Process finished with exit code 0

四、compareTo 源码说明

String比较用 compareTo 方法,针对参与比较的不同的字符,从第一位开始往后比较,然后返回相应的 int 值:

  1. 字符个数相同,返回参与比较的前后两个字符串的 ASSIC 码差值。
  2. 两个字符串首字母不同,则该方法返回首字母的 ASSIC 码差值。
  3. 参与比较的两个字符串如果首字符相同,则比较下一个字符,直到有不同的为止,返回该不同的字符的 ASSIC 码差值。
  4. 两个字符串不一样长,可以参与比较的字符又完全一样,则返回两个字符串的长度差值。
  5. 目前 compareTo 项目中的用途是比较版本号的高低。
private final char value[]; 
public int compareTo(String anotherString) {
     int len1 = value.length;
     int len2 = anotherString.value.length;
     int lim = Math.min(len1, len2);
     char v1[] = value;
     char v2[] = anotherString.value;

     int k = 0;
     while (k < lim) {
         char c1 = v1[k];
         char c2 = v2[k];
         if (c1 != c2) {
             return c1 - c2;
         }
         k++;
     }
     return len1 - len2;
}

Java 中的 compareto 方法实例

public static void main(String[] args) {
      //返回参与比较的前后两个字符串的ASSIC码的差值
       String a = "a";//97
       String b = "b";//98
       System.out.println("a.compareTo(b):" + a.compareTo(b));//-1
       System.out.println("b.compareTo(a):" + b.compareTo(a));//1

       String c = "c";
       String c1 = "c";
       System.out.println("c.compareTo(c1):" + c.compareTo(c1));//0

       //两个字符串首字母不同,则该方法返回首字母的ASSIC码的差值
       String d = "abc";
       String e = "bcdfg";
       System.out.println("d.compareTo(e):" + d.compareTo(e));//-1

       //参与比较的两个字符串如果首字符相同,则比较下一个字符,
       //直到有不同的为止,返回该不同的字符的ASSIC码差值
       String g = "abedfg";
       System.out.println("d.compareTo(g):" + d.compareTo(g));//-2
       //两个字符串不一样长,可以参与比较的字符又完全一样,
       //则返回两个字符串的长度差值
       String h = "abcdefg";
       System.out.println("d.compareTo(h):" + d.compareTo(h));//-4
       String i = "ab";
       System.out.println("d.compareTo(i):" + d.compareTo(i));//1
       //目前compareTo项目中的用途是比较版本号的高低
       String num = "1.0.0";
       String val = "1.0.1";
       System.out.println("num.compareTo(val):" + num.compareTo(val));//-1
  }

结果如下:

面试

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

public class Solution {
    public String PrintMinNumber(int [] numbers) {
        if(numbers.length==0) {
            return "";
        }
        int len = numbers.length;
        String[] str = new String[len];
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<len;i++) {
            str[i] = String.valueOf(numbers[i]);
        }
        Arrays.sort(str,new Comparator<String>(){
            @Override
            public int compare(String o1,String o2){
                String c1 = o1 + o2;
                String c2 = o2 + o1;
                return c1.compareTo(c2);
            }
        });
        for(int i=0;i<len;i++) {
            sb.append(str[i]);
        }
        return sb.toString();
    }
}

到此这篇关于Java中的排序与内部比较器Compareable解析的文章就介绍到这了,更多相关Java中的排序内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文