Java中使用Comparable和Comparator实现字段排序功能

Comparable 简介

Comparable 是排序接口。

若一个类实现了Comparable接口,就意味着“该类支持排序”。  即然实现Comparable接口的类支持排序,假设现在存在“实现Comparable接口的类的对象的List列表(或数组)”,则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序。

此外,“实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。

Comparable 定义

Comparable 接口仅仅只包括一个函数,它的定义如下:

package java.lang;
import java.util.*;

public interface Comparable<T> {
    public int compareTo(T o);
}

说明:
假设我们通过 x.compareTo(y) 来“比较x和y的大小”。若返回“负数”,意味着“x比y小”;返回“零”,意味着“x等于y”;返回“正数”,意味着“x大于y”。

 

 


Comparator 简介

Comparator 是比较器接口。

我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,我们可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序。

 

Comparator 定义

Comparator 接口仅仅只包括两个函数,它的定义如下:

package java.util;

public interface Comparator<T> {

    int compare(T o1, T o2);

    boolean equals(Object obj);
}

说明:
(01) 若一个类要实现Comparator接口:它一定要实现compareTo(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。

        为什么可以不实现 equals(Object obj) 函数呢? 因为任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;所以,其它所有的类也相当于都实现了该函数。

(02) int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。



Comparator 和 Comparable 比较

Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。

而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。我们不难发现:Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。

 


Comparator 和 Comparable 实例讲解

首先我们定义一个实现Comparable的接口的实体类,Person:

package com.boonya.program.compare;

/**
 * 可比较排序Person
 * 
 * @package com.boonya.program.compare.ComparablePerson
 * @date 2016年12月23日 下午10:36:10
 * @author pengjunlin
 * @comment
 * @update
 */
public class Person implements Comparable<Person> {

	private long id;
	private String name;
	private int age;
	private String email;
	private String address;

	public Person(long id, String name, int age, String email,
			String address) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
		this.email = email;
		this.address = address;
	}

	public Person() {
		super();
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	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 String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	@Override
	public int compareTo(Person o) {
		if (this == o) {
			return 0;
		} else if (o != null && o instanceof Person) {
			Person u = (Person) o;
			if (id < u.id) {
				return -1;
			}else if(id == u.id){
				return 0;
			} else {
				return 1;
			}
		} else {
			return -1;
		}
	}

	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + ", age=" + age
				+ ", email=" + email + ", address=" + address + "]";
	}
	
	

}
然后定义一个排序枚举类,SortEnum:

package com.boonya.program.compare;
/**
 * 排序类型枚举
 * 
 * @package com.boonya.program.compare.SortEnum
 * @date   2016年12月23日  上午10:58:44
 * @author pengjunlin
 * @comment   
 * @update
 */
public enum SortEnum {
	
	/**
	 * 升序
	 */
	ASC(0),
	/**
	 * 降序
	 */
	DESC(1);
	
	private int value=0;

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}

	private SortEnum(int value) {
		this.value = value;
	}
	
	

}

定义Person的比较器,PersonComparator:

package com.boonya.program.compare;

import java.util.Comparator;
/**
 * 比较器PersonComparator
 * 
 * @package com.boonya.program.compare.PersonComparator
 * @date   2016年12月23日  上午10:56:53
 * @author pengjunlin
 * @comment   
 * @update
 */
public class PersonComparator implements Comparator<Person> {

	private String sortFieldName;

	private SortEnum sortEnum;

	public String getSortFieldName() {
		return sortFieldName;
	}

	public void setSortFieldName(String sortFieldName) {
		this.sortFieldName = sortFieldName;
	}

	public SortEnum getSortEnum() {
		return sortEnum;
	}

	public void setSortEnum(SortEnum sortEnum) {
		this.sortEnum = sortEnum;
	}

	public PersonComparator() {
		super();
	}

	public PersonComparator(SortEnum sortEnum) {
		super();
		this.sortEnum = sortEnum;
	}

	public PersonComparator(String sortFieldName, SortEnum sortEnum) {
		super();
		this.sortFieldName = sortFieldName;
		this.sortEnum = sortEnum;
	}

	@Override
	public int compare(Person o1, Person o2) {
		if (sortFieldName == null) {
			if (sortEnum == SortEnum.DESC) {
				return o2.compareTo(o1);
			} else {
				return o1.compareTo(o2);
			}
		} else {
			if (sortEnum == SortEnum.DESC) {
				if (sortFieldName.equals("id")) {
					long val = o2.getId() - o1.getId();
					if (val < 0)
						return 1;
					else if (val > 0)
						return -1;
					else
						return 0;
				} else if (sortFieldName.equals("name")) {
					return o2.getName().compareTo(o1.getName());
				} else if (sortFieldName.equals("age")) {
					int val = o2.getAge() - o1.getAge();
					if (val < 0)
						return 1;
					else if (val > 0)
						return -1;
					else
						return 0;
				} else if (sortFieldName.equals("email")) {
					return o2.getEmail().compareTo(o1.getEmail());
				} else if (sortFieldName.equals("address")) {
					return o2.getAddress().compareTo(o1.getAddress());
				}
				return o2.compareTo(o1);
			} else {
				if (sortFieldName.equals("id")) {
					long val = o1.getId() - o2.getId();
					if (val > 0)
						return 1;
					else if (val < 0)
						return -1;
					else
						return 0;
				} else if (sortFieldName.equals("name")) {
					return o1.getName().compareTo(o2.getName());
				} else if (sortFieldName.equals("age")) {
					int val = o1.getAge() - o2.getAge();
					if (val > 0)
						return 1;
					else if (val < 0)
						return -1;
					else
						return 0;
				} else if (sortFieldName.equals("email")) {
					return o1.getEmail().compareTo(o2.getEmail());
				} else if (sortFieldName.equals("address")) {
					return o1.getAddress().compareTo(o2.getAddress());
				}
				return o1.compareTo(o2);
			}

		}

	}

}

最后测试一下我们的排序,PersonComparatorTest:

package com.boonya.program.compare;

import java.util.ArrayList;
import java.util.Collections;
import org.junit.Test;
/**
 * 比较器测试
 * 
 * @package com.boonya.program.compare.PersonComparatorTest
 * @date   2016年12月23日  上午10:58:06
 * @author pengjunlin
 * @comment   
 * @update
 */
public class PersonComparatorTest {

	@Test
	public void sortAscTest() {
		ArrayList<Person> persons = new ArrayList<Person>();
		Person p1 = new Person(1, "Allen", 12, "Allen@support.com", "New York");
		Person p2 = new Person(5, "Bob", 9, "Bob@support.com", "Atlantic");
		Person p3 = new Person(2, "Author", 20, "Author@support.com", "Washington");
		Person p4 = new Person(3, "Peter", 30, "Peter@support.com", "Los Angilis");
		Person p5 = new Person(4, "Mercy", 21, "Mercy@support.com", "San Fransisco");
		persons.add(p1);
		persons.add(p2);
		persons.add(p3);
		persons.add(p4);
		persons.add(p5);
		
		System.out.println(">>>>========sort by id asc===========<<<<"); 
		PersonComparator comparator=new PersonComparator(SortEnum.ASC);

		Collections.sort(persons, comparator);// 用我们写好的Comparator对persons进行排序
		for (int i = 0; i < persons.size(); i++) {
			System.out.println(persons.get(i).toString());
		}
	}
	
	@Test
	public void sortDescTest() {
		ArrayList<Person> persons = new ArrayList<Person>();
		Person p1 = new Person(1, "Allen", 12, "Allen@support.com", "New York");
		Person p2 = new Person(5, "Bob", 9, "Bob@support.com", "Atlantic");
		Person p3 = new Person(2, "Author", 20, "Author@support.com", "Washington");
		Person p4 = new Person(3, "Peter", 30, "Peter@support.com", "Los Angilis");
		Person p5 = new Person(4, "Mercy", 21, "Mercy@support.com", "San Fransisco");
		persons.add(p1);
		persons.add(p2);
		persons.add(p3);
		persons.add(p4);
		persons.add(p5);
		
		System.out.println(">>>>========sort by id desc===========<<<<"); 
		PersonComparator comparator=new PersonComparator(SortEnum.DESC);

		Collections.sort(persons, comparator);// 用我们写好的Comparator对persons进行排序
		for (int i = 0; i < persons.size(); i++) {
			System.out.println(persons.get(i).toString());
		}
	}
	
	@Test
	public void sortByFieldAscTest() {
		ArrayList<Person> persons = new ArrayList<Person>();
		Person p1 = new Person(1, "Allen", 12, "Allen@support.com", "New York");
		Person p2 = new Person(5, "Bob", 9, "Bob@support.com", "Atlantic");
		Person p3 = new Person(2, "Author", 20, "Author@support.com", "Washington");
		Person p4 = new Person(3, "Peter", 30, "Peter@support.com", "Los Angilis");
		Person p5 = new Person(4, "Mercy", 21, "Mercy@support.com", "San Fransisco");
		persons.add(p1);
		persons.add(p2);
		persons.add(p3);
		persons.add(p4);
		persons.add(p5);
		
		System.out.println(">>>>========sort by name asc===========<<<<"); 
		PersonComparator comparator=new PersonComparator("name",SortEnum.ASC);

		Collections.sort(persons, comparator);// 用我们写好的Comparator对persons进行排序
		for (int i = 0; i < persons.size(); i++) {
			System.out.println(persons.get(i).toString());
		}
		
		System.out.println(">>>>========sort by age asc===========<<<<"); 
		PersonComparator comparator2=new PersonComparator("age",SortEnum.ASC);

		Collections.sort(persons, comparator2);// 用我们写好的Comparator对persons进行排序
		for (int i = 0; i < persons.size(); i++) {
			System.out.println(persons.get(i).toString());
		}
		
		System.out.println(">>>>========sort by email asc===========<<<<"); 
		PersonComparator comparator3=new PersonComparator("email",SortEnum.ASC);

		Collections.sort(persons, comparator3);// 用我们写好的Comparator对persons进行排序
		for (int i = 0; i < persons.size(); i++) {
			System.out.println(persons.get(i).toString());
		}
		
		System.out.println(">>>>========sort by address asc===========<<<<"); 
		PersonComparator comparator4=new PersonComparator("address",SortEnum.ASC);

		Collections.sort(persons, comparator4);// 用我们写好的Comparator对persons进行排序
		for (int i = 0; i < persons.size(); i++) {
			System.out.println(persons.get(i).toString());
		}
	}

}

输出结果:

>>>>========sort by id asc===========<<<<
Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]
Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
>>>>========sort by id desc===========<<<<
Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]
Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
>>>>========sort by name asc===========<<<<
Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]
Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
>>>>========sort by age asc===========<<<<
Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]
Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
>>>>========sort by email asc===========<<<<
Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]
Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
>>>>========sort by address asc===========<<<<
Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]

使用反射的方式实现List集合对象任意字段排序

参考:https://github.com/Tony36051/sortByField/blob/master/src/main/java/SortByField.java

package main.java;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class SortByField {

	static class Cmp<E> implements Comparator<E> {
		Method getMethod = null;
		Field fieldToGet = null;
		MethodHandle cmpMethodHandle;
		// Method cmpMethod ;

		Cmp(MethodHandle cmpMethodHandle, /* Method cmpMethod, */Method getMethod) {
			this.getMethod = getMethod;
			this.cmpMethodHandle = cmpMethodHandle;
			// this.cmpMethod = cmpMethod;
		}

		Cmp(MethodHandle cmpMethodHandle, Field fieldToGet) {
			this.cmpMethodHandle = cmpMethodHandle;
			this.fieldToGet = fieldToGet;
		}

		@Override
		public int compare(E o1, E o2) {
			if (o2 == null) {
				return -1;
			} else if (o1 == null) {
				return 1;
			}
			try {
				if (getMethod != null) {
					return (int) cmpMethodHandle.invokeExact((Comparable<?>) getMethod.invoke(o1),
							getMethod.invoke(o2));
					// return (int)cmpMethod.invoke(getMethod.invoke(o1),
				}
				if (fieldToGet != null) {
					return (int) cmpMethodHandle.invokeExact((Comparable<?>) fieldToGet.get(o1), fieldToGet.get(o2));
				}
			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				System.out.println("If sortByField() isn't modifie, it won't print errorStackTrace. Default return 0");
				e.printStackTrace();
			} catch (Throwable e) {
				e.printStackTrace();
			}
			return 0;
		}
	}

	/**
	 * sort a list containing JavaBean according specific key( field ).  
	 * Mostly, sortByField take ~1.5 times as much as Traditional implementation when list.size() > 100K
	 * 
	 * @param list:
	 *            list to be sorted
	 * @param fieldName:
	 *            sort list according this field
	 * @param order:
	 *            asc(default) or desc
	 * @author Tony
	 * @email 360517703@163.com
	 * @Time 2015-08-14 11:12
	 */
	public static <E> void sortByField(List<E> list, String fieldName, String order) {
		if (list == null || list.size() < 2) { // no need to sort
			return;
		}
		if (fieldName == null || fieldName.trim().equals(""))
			return; // won't sort if fieldName is null or ""
		// get actual class of generic E
		Class<?> eClazz = null; // use reflect to get the actual class
		boolean isAllNull = true; // default all elements are null
		for (E e : list) {
			if (e != null) {
				isAllNull = false;
				eClazz = e.getClass();
				break;
			}
		}
		if (isAllNull)
			return; // no need to sort, because all elements are null
		// check fieldName in Class E
		Field keyField = null; // the <fieldName> Field as sort key
		try {
			keyField = eClazz.getDeclaredField(fieldName);
		} catch (NoSuchFieldException e1) {
			e1.printStackTrace();
			System.out.println("The List<E> doesn't contain fieldName. That is "
					+ String.format("%s has no Field %s.", eClazz, fieldName));
		} catch (SecurityException e1) {
			e1.printStackTrace();
			System.out.println("deny access to  class or field.");
		}
		// check field is either Comparable
		Class<?> fieldClazz = keyField.getType();
		boolean isComparable = Comparable.class.isAssignableFrom(fieldClazz);
		if (isComparable == false)
			return; // if the class of fieldName is not comparable, don't sort

		// try to use getter method to get field first. Because a little faster
		// than Field.get(Object)
		StringBuilder getterName; // adapt to JavaBean getter method
		if (fieldClazz.getSimpleName().equals("Boolean")) {
			getterName = new StringBuilder("is");
		} else {
			getterName = new StringBuilder("get");
		}
		char[] cs = fieldName.toCharArray();
		if (cs[0] >= 'a' && cs[0] <= 'z')
			cs[0] -= 32; // change the first char to lowerCase
		getterName.append(cs);
		Method getterMethod = null;
		try {
			getterMethod = eClazz.getDeclaredMethod(getterName.toString());
		} catch (NoSuchMethodException | SecurityException e1) {
			// System.out.println("Field " + fieldName + " has no " + getterName
			// + "() . ");
			// e1.printStackTrace();
		}
		/*
		 * // get compare method for specified field. //Abandoned. Because
		 * MethodHandle.invokeExact() is a little faster than Method.invoke()
		 * Method cmpMethod = null; try { cmpMethod =
		 * fieldClazz.getDeclaredMethod("compareTo", fieldClazz); } catch
		 * (NoSuchMethodException | SecurityException e1) { System.out.println(
		 * "deny access to class or method(comparaTo).\nImpossible to show errorStrackTrace Because of Comparable check"
		 * ); e1.printStackTrace(); cmpMethod.setAccessible(true); }
		 */
		Cmp<E> cmp;
		MethodHandles.Lookup lookup = MethodHandles.lookup();
		MethodType type = MethodType.methodType(int.class, Object.class);
		MethodHandle mh = null;
		try {
			mh = lookup.findVirtual(Comparable.class, "compareTo", type);
		} catch (NoSuchMethodException | IllegalAccessException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		if (getterMethod != null) {
			// cmpMethod.setAccessible(true);
			getterMethod.setAccessible(true);
			cmp = new Cmp<E>(mh, getterMethod);
		} else { // if cannot find getter method, use reflect to get specified
					// field
			keyField.setAccessible(true);
			cmp = new Cmp<E>(mh, keyField);
		}

		if (order.equalsIgnoreCase("desc")) {
			Collections.sort(list, Collections.reverseOrder(cmp));
			return;
		}
		Collections.sort(list, cmp);
	}

	/**
	 * sort a list containing JavaBean according specific key( field ) order by
	 * ascend.  
	 * 
	 *  Mostly, sortByField take ~1.5 times as much as Traditional implementation when list.size() > 100K
	 * @param list
	 *            list to be sort
	 * @param fieldName
	 *            sort list according this field
	 * @author Tony
	 * @email 360517703@163.com
	 * @Time 2015-08-14 11:12
	 */
	public static <E> void sortByField(List<E> list, String fieldName) {
		sortByField(list, fieldName, "asc");
	}

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值