实现一个通用的中英文排序工具
2023-04-18 14:40:21 时间
前言
利用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
这类函数来进行排序,也比较方便。
相关文章
- 浅谈:国内软件公司为何无法做大做强?
- NHibernate延迟加载机制 - NHibernate 2.1.2
- 《好的程序象一首诗》(2010/2/8)
- 时间管理——如何应对外界的干扰
- 程序员的品味
- 全世界最短的IE判定
- NHibernate3剖析:Configuration篇之Cache lambda-configuration配置
- 【Silverlight】Bing Maps系列文章汇总
- 将ASP.NET MVC 2.0 部署在IIS6和IIS7上的教程
- 程序员30过后
- 代码审查――为可读性努力的巨大能量
- 十年磨一剑—序
- C# 4.0 新特性-dynamic
- 101与金根回顾敏捷个人:(17)技术研究之道
- [你必须知道的.NET]第三十五回,判断dll是debug还是release,这是个问题
- 基于自然语言的软件工程和程序设计(上)
- .NET 4.0中数组的新增功能
- 读《输赢》所想到的——关于CRM和团队管理
- 101与金根回顾敏捷个人:(15) 从小工到专家
- 【译】利用Asp.net MVC处理文件的上传下载