List集合排序、自定义比较器排序

List集合排序

1、集合工具类(包装类)Collections.sort

使用Collections提供的sort方法,对集合进行排序

Collections.sort(list);

实现原理:
将list转成了数组对象后,调用了Array的sort方法,将数组排序,再用list的迭代器(注意不是collection迭代器)一个个得赋值回去,就使得传入的list变成了一个有序的list

  • 由于set,map都有SortedSet,TreeSet和SortedMap实现类,所以Collections中并没有对set和map支持sort方法
  • sort方法默认的顺序是升序,如果你想要降序排,你可以调用Collections中的另一个sort方法
public class SortListDemo {
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();
		Random random = new Random();
		//随机生成一个数,作为list元素
		for(int i=0;i<10;i++){
			//向末尾添加随机的数
			list.add(random.nextInt(100));
		}
		System.out.println(list);
		//使用Collections提供的sort方法,对集合进行排序
		Collections.sort(list);
		System.out.println(list);
	}
}

Collection 与 Collections 的区别

1. java.util.Collection 是一个集合接口

它提供了对集合对象进行基本操作的通用接口方法
Collection接口在Java 类库中有很多具体的实现
Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式

2. java.util.Collections 是一个包装类

它包含有各种有关集合操作的静态多态方法
此类不能实例化,就像一个工具类,服务于Java的Collection框架

2、自定义排序

1. 比较器

Comparable接口:

Collections的sort方法是对集合元素进行自然排序
在使用Collections的sort排序的集合元素都必须是Comparable接口的实现类
该接口表示其子类是可比较的,因为实现该接口必须重写抽象方法 int compareTo(T t);

这个接口用于给需要排序的类来实现的,一旦实现,这个类就会按这个方式排序
比如说我们常见的String类就实现了这个接口,并重写了里面的compareTo
所以我们对字符串可以利用compareTo方法进行排序

Comparator接口(一般用这种):

一旦Java类实现了Comparable接口,其比较逻辑就已经确定(只能按写在自定义类中的算法排序)
如果希望在排序的操作中临时指定比较规则,可以采用Comparator接口回调的方式。
Comparator接口要求实现类必须重写其定义的方法 int compare(T o1,T o2)

通过比较器比较自定义类

自定义排序必须提供比较器
比较器为一个自定义的实现了比较器的类的实例化对象

自定义排序语法
Collections.sort(list,比较器);

2.实现方式

① 自定义一个比较器类,实现比较器的接口(Comparator 尖括号里面传入比较器比较的类)

注意:必须重写compare方法,返回值为int

然后使用Collections.sort时,在括号里传入需要排序的列表和new一个自定义比较器类的实例对象

Collections.sort(list, new ByAge());

② 匿名内部类(上班后常用,记得简写),实现原理同上

        //使用匿名内部类排序
		Comparator<Person> byAge = new Comparator<Person>(){
			@Override
			public int compare(Person o1, Person o2) {
				return o1.age-o2.age;
			}
		};
		//按照匿名内部类比较器规则进行年龄排序
		//这里只需要把刚才的内部类对象传递进来就行了
		Collections.sort(list,byAge);
		System.out.println(list);
		
		//上班后上述匿名内部类代码可以简化为
		Collections.sort(list,new Comparator<Person>(){
			public int compare(Person o1, Person o2){
				return o1.age-o2.age;
			}
		});
		System.out.println(list);
		
		//匿名内部类实现,按名字顺序比较
		Collections.sort(list,new Comparator<Person>(){
			public int compare(Person o1, Person o2) {
				//compareTo 按字典顺序比较两个字符串(字符编码)
				return o1.name.compareTo(o2.name);
			}
		});
		System.out.println(list);

3、自定义排序代码

package collection.comparator;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 自定义排序(通过比较器比较自定义类)
 * @author Tian
 *
 */
public class SortListDemo {
	public static void main(String[] args) {
		/*
		 * 对一组人进行自定义排序
		 */
		List<Person> list = new ArrayList<>();
		list.add(new Person("徐凤年",23,130));
		list.add(new Person("姜泥",20,96));
		list.add(new Person("裴南苇",32,98));
		list.add(new Person("洛阳",800,100));
		/*
		 * 自定义排序必须提供比较器
		 * 比较器为一个自定义的实现了比较器的类的实例化对象
		 */
		//Collections.sort(list,比较器);
		
		//按照自定义比较器规则进行年龄排序
		Collections.sort(list, new ByAge());
		System.out.println(list);
		
		//按照自定义比较器规则进行体重排序
		Collections.sort(list, new ByWeight());
		System.out.println(list);
		
		//使用匿名内部类排序
		Comparator<Person> byAge = new Comparator<Person>(){
			@Override
			public int compare(Person o1, Person o2) {
				return o1.age-o2.age;
			}
		};
		//按照匿名内部类比较器规则进行年龄排序
		//这里只需要把刚才的内部类对象传递进来就行了
		Collections.sort(list,byAge);
		System.out.println(list);
		
		//上班后上述匿名内部类代码可以简化为
		Collections.sort(list,new Comparator<Person>(){
			public int compare(Person o1, Person o2){
				return o1.age-o2.age;
			}
		});
		System.out.println(list);
		
		//匿名内部类实现,按名字顺序比较
		Collections.sort(list,new Comparator<Person>(){
			public int compare(Person o1, Person o2) {
				//compareTo 按字典顺序比较两个字符串(字符编码)
                //详情见拓展
				return o1.name.compareTo(o2.name);
			}
		});
		System.out.println(list);
		
		//按名字拼音的首字母排序
		//创建了一个字符串的列表,用来接收人类中的姓名
		List<String> str = new ArrayList<>();
		//循环遍历人类列表,将姓名放到字符串列表中
		for(Person p : list){
			str.add(p.name);
		}
		//使用比较器对名字列表进行排序
		/*
		 * Collator.getInstance(java.util.Locale.CHINA)
		 * 这个方法是
		 * Collator类调用了一下他的工厂方法(静态方法的一种常见用途)
		 * 该方法会返回一个比较器的对象,内部实现了按字符串首字符的拼音首字母排序
		 * 这个方法需要传入一个参数,语言包,我们这里传入的是中文
		 */
		Collections.sort(str,Collator.getInstance(java.util.Locale.CHINA));
		//输出排序后的字符串列表
		System.out.println(str);
		//问题:只能修改String列表,怎么把这个修改顺序的影响扩大到Person列表中呢
	}
}

/**
 * 根据年龄比较两人的比较器
 * 自定义比较器,实现比较器的接口
 * Comparator
 * 尖括号里面传入比较器比较的类
 */
class ByAge implements Comparator<Person>{
	/**
	 * 重写接口里面的比较方法
	 * 注意:该方法返回值需要有三个状态(0、正数、负数)
	 */
	@Override
	public int compare(Person o1, Person o2) {
		//按照两个人的年龄比较
//		if(o1.age == o2.age){
//			return 0; //返回0,表示人类o1与o2年龄一样
//		}else if(o1.age > o2.age){
//			return 1; //返回正数,表示01大于o2年龄
//		}else{
//			return -1; //返回负数,表示o1年龄小于o2年龄
//		}
		//可以优化为
		return o1.age-o2.age;
		//倒叙可以写为
		//return -(o1.age-o2.age);
	}
}

/**
 * 根据体重排序的比较器
 */
class ByWeight implements Comparator<Person>{
	/**
	 * 比较体重的比较器
	 * 体重是double类型,强转可能精度缺失(-0.5会变为0)
	 * 所以使用Math.signum用来取差的符号
	 * static double	signum(double d)
	 * 如果参数为零,则为零;如果参数大于零则为1.0;如果参数小于零,则为-1.0
	 * 获取符号后,我们可以进行强转,不会影响精度了
	 */
	@Override
	public int compare(Person o1, Person o2) {
		return (int) Math.signum(o1.weight-o2.weight);
	}
}

/**
 * 自定义类:人类(供测试用)
 * @author Tian
 *
 */
class Person{
	String name;
	int age;
	double weight;
	public Person(String name, int age, double weight) {
		this.name = name;
		this.age = age;
		this.weight = weight;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", weight=" + weight + "]";
	}
	
}

4、拓展

1. 字符串大小比较问题

字符串提供比较大小的方法
int compareTo(String anotherString)
按字典顺序比较两个字符串(按照字符编码进行比较)

比较两个字符串时:
先比较前两个字符大小(第一个汉字),如果可以比较出来就返回,不行的话就比较第二个
返回0,表示相等;返回正数表示第一个字符串大;返回负数,表示第一个字符串小

先比较前两个字符大小(第一个汉字),如果可以比较出来就返回,不行的话就比较第二个

public static void main(String[] args) {
		String s1 = "徐凤年";
		String s2 = "邓太阿";
		String s3 = "曹长卿";
		int n = s1.compareTo(s2);
		System.out.println(n);
	}

2. 按字符串首字符的拼音首字母排序

使用比较器对名字列表进行排序
Collator.getInstance(java.util.Locale.CHINA)
这个方法是
Collator类调用了一下他的工厂方法(静态方法的一种常见用途)
该方法会返回一个比较器的对象,内部实现了按字符串首字符的拼音首字母排序
这个方法需要传入一个参数,语言包,我们这里传入的是中文

        //按名字拼音的首字母排序
		//创建了一个字符串的列表,用来接收人类中的姓名
		List<String> str = new ArrayList<>();
		//循环遍历人类列表,将姓名放到字符串列表中
		for(Person p : list){
			str.add(p.name);
		}
		//使用比较器对名字列表进行排序
		/*
		 * Collator.getInstance(java.util.Locale.CHINA)
		 * 这个方法是
		 * Collator类调用了一下他的工厂方法(静态方法的一种常见用途)
		 * 该方法会返回一个比较器的对象,内部实现了按字符串首字符的拼音首字母排序
		 * 这个方法需要传入一个参数,语言包,我们这里传入的是中文
		 */
		Collections.sort(str,Collator.getInstance(java.util.Locale.CHINA));
		//输出排序后的字符串列表
		System.out.println(str);
		//问题:只能修改String列表,怎么把这个修改顺序的影响扩大到Person列表中呢

ps.工厂方法

静态方法的一种常见用途,用于构造对象

工厂方法的返回值为一个类的对象,用于使用一个类的静态方法完成创建一个我们需要的对象

比如上面的Collator.getInstance() 就是Collator中的静态方法,用来创建一个比较器对象

你可能感兴趣的:(java数据结构)