对List进行排序
当我们需要对集合的元素进行排序的时候,可以使用java.util.Comparator 创建一个比较器来进行排序。Comparator接口同样也是一个函数式接口,我们可以把使用lambda表达式。如下示例
public class ComparatorLearn {
public static void main(String[] args) {
Employee e1 = new Employee("John", 25, 3000, 9922001);
Employee e2 = new Employee("Ace", 22, 2000, 5924001);
Employee e3 = new Employee("Keith", 35, 4000, 3924401);
List<Employee> employees = new ArrayList<>();
employees.add(e1);
employees.add(e2);
employees.add(e3);
/**
* @SuppressWarnings({"unchecked", "rawtypes"})
* default void sort(Comparator<? super E> c) {
* Object[] a = this.toArray();
* Arrays.sort(a, (Comparator) c);
* ListIterator<E> i = this.listIterator();
* for (Object e : a) {
* i.next();
* i.set((E) e);
* }
* }
*
* sort 对象接收一个 Comparator 函数式接口,可以传入一个lambda表达式
*/
System.out.println("################");
employees.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
Collections.sort(employees, (o1, o2) -> o1.getName().compareTo(o2.getName()));
employees.forEach(System.out::println);
/**
* Comparator.comparing 方法的使用
* comparing 方法接收一个 Function 函数式接口 ,通过一个 lambda 表达式传入
*/
System.out.println("################");
employees.sort(Comparator.comparing(e -> e.getName()));
employees.forEach(System.out::println);
/**
* 该方法引用 Employee::getAge 可以代替 lambda表达式
*/
System.out.println("################");
employees.sort(Comparator.comparing(Employee::getAge));
employees.forEach(System.out::println);
/**
* 相反的排序规则
*/
System.out.println("################");
Collections.sort(employees, Comparator.comparing(Employee::getName).reversed());
employees.forEach(System.out::println);
/**
* 使用 Comparator.thenComparing 排序
*/
System.out.println("################");
Collections.sort(employees, Comparator.comparing(Employee::getAge).thenComparing(Employee::getName));
employees.forEach(System.out::println);
/**
* 使用 Comparator.nullsFirst进行排序
*/
System.out.println("################");
employees.add(null);
Collections.sort(employees, Comparator.nullsFirst(Comparator.comparing(Employee::getName)));
employees.forEach(System.out::println);
/**
* 使用 Comparator.nullsLast进行排序
*/
System.out.println("################");
Collections.sort(employees, Comparator.nullsLast(Comparator.comparing(Employee::getName)));
employees.forEach(System.out::println);
}
}
public class Employee {
String name;
int age;
double salary;
long mobile;
// constructors, getters & setters
public Employee(String name, int age, double salary, long mobile) {
this.name = name;
this.age = age;
this.salary = salary;
this.mobile = mobile;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public long getMobile() {
return mobile;
}
public void setMobile(long mobile) {
this.mobile = mobile;
}
@Override
public String toString() {
return "Employee [name=" + name + ", age=" + age + ", salary=" + salary + ", mobile=" + mobile + "]";
}
}
对Map进行排序
在讲解Map排序之前,我们先来稍微了解下map,map是键值对的集合接口,它的实现类主要包括:HashMap, TreeMap, Hashtable以及LinkedHashMap等。其中这四者的区别如下(简单介绍):
HashMap:我们最常用的Map,HashMap是无序的,它根据key的HashCode 值来存储数据,根据key可以直接获取它的Value,同时它具有很快的访问速度。HashMap最多只允许一条记录的key值为Null(多条会覆盖);允许多条记录的Value为 Null。非同步的。
TreeMap:能够把它保存的记录根据key排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。非同步的。
Hashtable:与HashMap类似,不同的是:key和value的值均不允许为null;它支持线程的同步,即任一时刻只有一个线程能写Hashtable, 因此也导致了Hashtale在写入时会比较慢。
LinkedHashMap:LinkedHashMap是有序的,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。key和value均允许为空,非同步的。
Java中Comparable和Comparator的区别
Comparable
比较者大于被比较者,返回正整数
比较者等于被比较者,返回0
比较者小于被比较者,返回负整数
public class Domain implements Comparable<Domain>
{
private String str;
public Domain(String str)
{
this.str = str;
}
public int compareTo(Domain domain)
{
if (this.str.compareTo(domain.str) > 0)
return 1;
else if (this.str.compareTo(domain.str) == 0)
return 0;
else
return -1;
}
public String getStr()
{
return str;
}
}
public static void main(String[] args)
{
Domain d1 = new Domain("c");
Domain d2 = new Domain("c");
Domain d3 = new Domain("b");
Domain d4 = new Domain("d");
System.out.println(d1.compareTo(d2));
System.out.println(d1.compareTo(d3));
System.out.println(d1.compareTo(d4));
}
运行结果为:
0
1
-1
Comparator
o1大于o2,返回正整数
o1等于o2,返回0
o1小于o3,返回负整数
public class DomainComparator implements Comparator<Domain>
{
public int compare(Domain domain1, Domain domain2)
{
if (domain1.getStr().compareTo(domain2.getStr()) > 0)
return 1;
else if (domain1.getStr().compareTo(domain2.getStr()) == 0)
return 0;
else
return -1;
}
}
public static void main(String[] args)
{
Domain d1 = new Domain("c");
Domain d2 = new Domain("c");
Domain d3 = new Domain("b");
Domain d4 = new Domain("d");
DomainComparator dc = new DomainComparator();
System.out.println(dc.compare(d1, d2));
System.out.println(dc.compare(d1, d3));
System.out.println(dc.compare(d1, d4));
}
看一下运行结果:
0
1
-1
因为泛型指定死了,所以实现Comparator接口的实现类只能是两个相同的对象(不能一个Domain、一个String)进行比较了,实现Comparator接口的实现类一般都会以"待比较的实体类+Comparator"来命名
总结
如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,写比较算法。
实现Comparable接口的方式比实现Comparator接口的耦合性要强一些,如果要修改比较算法,要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。
- 对于一些普通的数据类型(比如 String, Integer, Double…),它们默认实现了Comparable 接口,实现了 compareTo 方法,我们可以直接使用。
- 而对于一些自定义类,它们可能在不同情况下需要实现不同的比较策略,我们可以新创建 Comparator 接口,然后使用特定的 Comparator 实现进行比较。
网友评论