「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战」
前言
利用Collator类可以轻松实现排序,但是我们可能有各种model都需要进行排序,这样就会有一个问题,如果单独为每个model写一段排序代码,代码重复量很大。
所以我打算写一个通用的工具,使用泛型+注解+反射的方式来解决。
注解
首先创建注解类
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Documented
public @interface SortString {
}
复制代码
这个注解的作用就是标识哪个字段或函数是用来排序。
工具类
然后是排序工具类,在我这个工具中排序规则是:中文 > 数字 > 英文,这是我们app的需求,大家可以根据自己的需求进行修改。
完整代码如下
public class SimplifiedChineseSorter {
private static final String SORTINGREGEX = "[^\\p{L}\\p{N}]+|^(The|A|An)\\b";
private static final Collator stringComparator = Collator.getInstance(Locale.SIMPLIFIED_CHINESE);
public static <T> List<T> sortByProvider(List<T> items, SortStringProvider<T> provider, boolean isIgnoreCase) {
if (items == null || items.size() <= 0) {
return null;
}
return sortList(items, provider, isIgnoreCase);
}
public static <T> List<T> sortByFieldName(List<T> items, String fieldName, boolean isIgnoreCase) {
if (items == null || items.size() <= 0) {
return null;
}
Field field = getSortStringField(items.get(0).getClass(), fieldName);
DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(field);
return sortList(items, provider, isIgnoreCase);
}
public static <T> List<T> sortByFieldAnnotation(List<T> items, boolean isIgnoreCase) {
if (items == null || items.size() <= 0) {
return null;
}
Field field = getSortStringField(items.get(0).getClass());
DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(field);
return sortList(items, provider, isIgnoreCase);
}
public static <T> List<T> sortByMethodName(List<T> items, String methodName, boolean isIgnoreCase) {
if (items == null || items.size() <= 0) {
return null;
}
Method method = getSortStringMethod(items.get(0).getClass(), methodName);
DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(method);
return sortList(items, provider, isIgnoreCase);
}
public static <T> List<T> sortByMethodAnnotation(List<T> items, boolean isIgnoreCase) {
if (items == null || items.size() <= 0) {
return null;
}
Method method = getSortStringMethod(items.get(0).getClass());
DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(method);
return sortList(items, provider, isIgnoreCase);
}
private static <T> List<T> sortList(List<T> items, final SortStringProvider<T> provider, final boolean isIgnoreCase) {
if(provider == null){
return items;
}
final List<T> chinieseList = new ArrayList<T>();
final List<T> nonChineseList = new ArrayList<T>();
for (T item : items) {
if (isChineseCharStart(format(provider.getSortString(item), isIgnoreCase))) {
chinieseList.add(item);
} else {
nonChineseList.add(item);
}
}
List<T> sortedChineseList = Ordering.from(new Comparator<T>() {
@Override
public int compare(T lhs, T rhs) {
return stringComparator.compare(format(provider.getSortString(lhs), isIgnoreCase), format(provider.getSortString(rhs), isIgnoreCase));
}
}).sortedCopy(chinieseList);
List<T> sortedNonChineseList = Ordering.from(new Comparator<T>() {
@Override
public int compare(T lhs, T rhs) {
return format(provider.getSortString(lhs), isIgnoreCase).compareTo(format(provider.getSortString(rhs), isIgnoreCase));
}
}).sortedCopy(nonChineseList);
sortedChineseList.addAll(sortedNonChineseList);
return sortedChineseList;
}
public static <T> Comparator<T> getSortComparatorByProvider(final Class clazz, SortStringProvider<T> provider, boolean isIgnoreCase) {
return getSortComparator(provider, isIgnoreCase);
}
public static <T> Comparator<T> getSortComparatorByFieldName(final Class clazz, final String fieldName, boolean isIgnoreCase) {
Field field = getSortStringField(clazz, fieldName);
DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(field);
return getSortComparator(provider, isIgnoreCase);
}
public static <T> Comparator<T> getSortComparatorByFieldAnnotation(final Class clazz, boolean isIgnoreCase) {
Field field = getSortStringField(clazz);
DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(field);
return getSortComparator(provider, isIgnoreCase);
}
public static <T> Comparator<T> getSortComparatorByMethodName(final Class clazz, final String methodName, boolean isIgnoreCase) {
Method method = getSortStringMethod(clazz, methodName);
DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(method);
return getSortComparator(provider, isIgnoreCase);
}
public static <T> Comparator<T> getSortComparatorByMethodAnnotation(final Class clazz, boolean isIgnoreCase) {
Method method = getSortStringMethod(clazz);
DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(method);
return getSortComparator(provider, isIgnoreCase);
}
private static <T> Comparator<T> getSortComparator(final SortStringProvider<T> provider, final boolean isIgnoreCase) {
return new Comparator<T>() {
@Override
public int compare(final T left, final T right) {
String leftStr = format(provider.getSortString(left), isIgnoreCase);
String rightStr = format(provider.getSortString(right), isIgnoreCase);
if (SimplifiedChineseSorter.isChineseCharStart(leftStr) &&
SimplifiedChineseSorter.isChineseCharStart(rightStr)) {
return stringComparator.compare(leftStr, rightStr);
} else {
return ComparisonChain.start()
.compareTrueFirst(SimplifiedChineseSorter.isChineseCharStart(leftStr),
SimplifiedChineseSorter.isChineseCharStart(rightStr))
.compare(leftStr, rightStr, Ordering.natural().nullsFirst())
.result();
}
}
};
}
public static boolean isChineseCharStart(String str) {
return !str.matches("[A-Za-z0-9\"“”]+.*");
}
private static <T> Field getSortStringField(Class<T> tClass) {
Field[] fields = tClass.getDeclaredFields();
if (fields != null) {
for (Field field : fields) {
if (field.isAnnotationPresent(SortString.class) && field.getType() == String.class) {
field.setAccessible(true);
return field;
}
}
}
Class superClass = tClass.getSuperclass();
if(superClass != null && !superClass.equals(Object.class)){
return getSortStringField(superClass);
}
throw new RuntimeException("The model doesn't have a @SortString field or the type of @SortString field is not a String");
}
private static <T> Field getSortStringField(Class<T> tClass, String sortFieldName) {
Field field = null;
try {
field = tClass.getDeclaredField(sortFieldName);
} catch (NoSuchFieldException e) {
}
finally {
if (field != null && field.getType() == String.class) {
field.setAccessible(true);
return field;
}
Class superClass = tClass.getSuperclass();
if(superClass != null && !superClass.equals(Object.class)){
return getSortStringField(superClass, sortFieldName);
}
throw new RuntimeException("The model doesn't have a field named " + sortFieldName);
}
}
private static <T> Method getSortStringMethod(Class<T> tClass) {
Method[] methods = tClass.getDeclaredMethods();
if (methods != null) {
for (Method method : methods) {
if (method.isAnnotationPresent(SortString.class) && method.getReturnType() == String.class) {
method.setAccessible(true);
return method;
}
}
}
Class superClass = tClass.getSuperclass();
if(superClass != null && !superClass.equals(Object.class)){
return getSortStringMethod(superClass);
}
throw new RuntimeException("The model doesn't have a @SortString method or the returnning type of @SortString method is not a String");
}
private static <T> Method getSortStringMethod(Class<T> tClass, String sortMethodName) {
Method method = null;
try {
method = tClass.getDeclaredMethod(sortMethodName);
} catch (NoSuchMethodException e) {
}
finally {
if (method != null && method.getReturnType() == String.class) {
method.setAccessible(true);
return method;
}
Class superClass = tClass.getSuperclass();
if(superClass != null && !superClass.equals(Object.class)){
return getSortStringMethod(superClass, sortMethodName);
}
throw new RuntimeException("The model doesn't have a method named " + sortMethodName);
}
}
private static String format(String data, boolean isIgnoreCase) {
Pattern pattern = Pattern.compile(SORTINGREGEX, Pattern.CASE_INSENSITIVE);
if(data == null){
return "";
}
else if(isIgnoreCase){
return pattern.matcher(data.trim()).replaceAll("").toLowerCase();
}
else{
return pattern.matcher(data.trim()).replaceAll("");
}
}
static class DefualtSortStringProvider<T> implements SortStringProvider<T>{
private Object orderBy;
DefualtSortStringProvider(Object orderBy){
this.orderBy = orderBy;
}
public String getSortString(T obj){
try {
if (orderBy instanceof Field) {
return (String) ((Field) orderBy).get(obj);
} else if (orderBy instanceof Method) {
return (String) ((Method)orderBy).invoke(obj);
}
}
catch (Exception e){
Log.e("SimplifiedChineseSorter", "getSortString", e);
}
return "";
}
}
public interface SortStringProvider<T>{
String getSortString(T obj);
}
}
复制代码
这个类比较大而全,提供了很多方法来进行排序,其中主要四个函数:
- sortByFieldName:通过给定的字段名对应的字段进行排序
- sortByFieldAnnotation:通过带有@SortString的字段进行排序
- sortByMethodName:通过给定的函数名对应的函数进行排序
- sortByMethodAnnotation:通过带有@SortString的函数进行排序
其中注意
- sortByMethodName要保证该类中没有同名函数,当然也可以加入验证函数参数签名来防止同名函数。
- 如果通过注释排序,如果类中有多个@SortString注释,只有第一个有效果。目前这个工具只针对单一维度排序,当然其实完全可以修改成多维度排序,可以增加一个优先级。不过我们需求没有这么复杂,所以就没有进行开发。
使用
使用很简单,只要在类中为那个用来排序的字段(或函数)加上@SortString注解即可,比如
public class Experience {
@SortString
public String name;
public int publicTime;
public String summy;
...
}
复制代码
然后就可以用SimplifiedChineseSorter.sortByFieldAnnotation
对Experience列表进行排序即可。
当然也可以用SimplifiedChineseSorter.sortByFieldName
,但是相对来说使用注释更加方便易于维护,之所以提供sortByFieldName
这类函数,主要是用于一些我们没有权限修改的类(比如三方库中的类),这样就没办法添加注释,就可以通过sortByFieldName
这类函数来进行排序,也比较方便。