实现一个通用的中英文排序工具

简介: 利用Collator类可以轻松实现排序,但是我们可能有各种model都需要进行排序,这样就会有一个问题,如果单独为每个model写一段排序代码,代码重复量很大。所以我打算写一个通用的工具,使用泛型+注解+反射的方式来解决。

前言


利用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的函数进行排序


其中注意


  1. sortByMethodName要保证该类中没有同名函数,当然也可以加入验证函数参数签名来防止同名函数。
  2. 如果通过注释排序,如果类中有多个@SortString注释,只有第一个有效果。目前这个工具只针对单一维度排序,当然其实完全可以修改成多维度排序,可以增加一个优先级。不过我们需求没有这么复杂,所以就没有进行开发。


使用


使用很简单,只要在类中为那个用来排序的字段(或函数)加上@SortString注解即可,比如


public class Experience {
    @SortString
    public String name;
    public int publicTime;
    public String summy;
    ...
}
复制代码


然后就可以用SimplifiedChineseSorter.sortByFieldAnnotation对Experience列表进行排序即可。

当然也可以用SimplifiedChineseSorter.sortByFieldName,但是相对来说使用注释更加方便易于维护,之所以提供sortByFieldName这类函数,主要是用于一些我们没有权限修改的类(比如三方库中的类),这样就没办法添加注释,就可以通过sortByFieldName这类函数来进行排序,也比较方便。



目录
相关文章
|
7月前
|
机器学习/深度学习 自然语言处理 安全
【网安专题11.8】14Cosco跨语言代码搜索代码: (a) 训练阶段 相关程度的对比学习 对源代码(查询+目标代码)和动态运行信息进行编码 (b) 在线查询嵌入与搜索:不必计算相似性
【网安专题11.8】14Cosco跨语言代码搜索代码: (a) 训练阶段 相关程度的对比学习 对源代码(查询+目标代码)和动态运行信息进行编码 (b) 在线查询嵌入与搜索:不必计算相似性
198 0
|
7月前
|
机器学习/深度学习 自然语言处理 算法
C++模板元模板(异类词典与policy模板)- - - 中篇后续
C++模板元模板(异类词典与policy模板)- - - 中篇后续
54 0
|
11月前
|
存储 编解码 算法
GIS开发:切片格式说明(翻译)-墨卡托
GIS开发:切片格式说明(翻译)-墨卡托
134 0
|
人工智能 自然语言处理
UDOP项目原作解读:统一视觉、语言、格式、任务的通用文档处理模型
UDOP项目原作解读:统一视觉、语言、格式、任务的通用文档处理模型
306 0
|
定位技术
ChatGPT 插件,组合后更妙
ChatGPT 插件,组合后更妙
Revit二次开发—内建类别BuiltInCategory枚举(中英文对照)
Revit二次开发—内建类别BuiltInCategory枚举(中英文对照)
|
Rust 自然语言处理 算法
【算法】1313. 解压缩编码列表(多语言实现)
给你一个以行程长度编码压缩的整数列表 nums 。 考虑每对相邻的两个元素 [freq, val] = [nums[2*i], nums[2*i+1]] (其中 i >= 0 ),每一对都表示解压后子列表中有 freq 个值为 val 的元素,你需要从左到右连接所有子列表以生成解压后的列表。 请你返回解压后的列表。
|
机器学习/深度学习 人工智能 自然语言处理
阿里云智能编码插件,Cosy文档搜索上新了
阿里云智能编码插件,Cosy文档搜索上新啦!阿里云智能编码插件,更Cosy的开发体验。Cosy集合了代码补全和代码搜索两大开发辅助功能,不仅能通过深度学习模型和程序分析技术,精准智能地帮助我们减少 击剑 击键次数,还能基于海量代码片段和社区问答数据,让我们可以哪里不会搜哪里,沉浸在IDE内快乐开发。
898 1
阿里云智能编码插件,Cosy文档搜索上新了
|
存储 自然语言处理 前端开发
基于solr实现通用:输入提示、纠错、拼音搜索、繁体搜索方案
假期重新把之前在新浪博客里面的文字梳理了下,搬到这里。文本介绍基于solr实现通用:输入提示、纠错、拼音搜索、繁体搜索方案。
331 0
基于solr实现通用:输入提示、纠错、拼音搜索、繁体搜索方案
|
自然语言处理 索引
CFStringTransform处理语言的强大工具 : 智能地处理用户的输入内容,经典应用场景【索引】
CFStringTransform处理语言的强大工具 : 智能地处理用户的输入内容,经典应用场景【索引】
177 0
CFStringTransform处理语言的强大工具 : 智能地处理用户的输入内容,经典应用场景【索引】