(小工具)Java反射机制实现任意对象集合排序并且获取排列名次的通用程序实践

本文介绍了一个Java通用排序工具,能够对任意对象集合按照指定字段进行排序,并为排序后的集合分配排名。工具使用反射机制实现,支持Long和Double类型的字段排序。

在应用开发的时候,我们有时候会遇到需要对一个给定集合进行排序的情况。如果该集合的数据记录是从数据库的获取,通常我们最简单的方案是直接在sql层面select后order by完成。 但是,假如某些场景下该数据不是从数据库获取呢?那就需要在应用程序层面进行排序。

假设给定一个集合,{5,4,3,6}, 需要对其进行排序,使其倒序排序后结果是 {6,5,4,3}。 实现方案之一可以使用Java集合框架提供的排序功能Collections.sort。

下面是简单的例子:
/*  public void sort(List<TspPefScoreCalTotal> list){
        Comparator<TspPefScoreCalTotal> comparator = new Comparator<TspPefScoreCalTotal>() {
            public int compare(TspPefScoreCalTotal s1, TspPefScoreCalTotal s2) {
                if (!s1.getTotalScore().equals(s2.getTotalScore())) {
                    return Double.compare(s2.getTotalScore(), s1.getTotalScore()); // 从大到小排,3,2,1
                } else {
                    return 0;
                }
            }
        };
        Collections.sort(list, comparator);
    }*/

但是,上面的例子就hardcode了List集合里的对象是TspPefScoreCalTotal,这样就使得程序不具有通用性。试试想,下一次我是对其它数据对象的某个字段进行排序呢? 那岂不是重复写上面相似的代码?

于是,尝试使用java的反射机制写一个通用程序,使其可以让调用者输入任意的对象,且可以任意指定按照哪个字段进行排序。下面给出具体实现例子,并且例子里实现了对排序后给出排列名次。

public class SortUtil<T> {

    String sortfield = "default";//
    String sortOrder = DESC;
    Class sortfieldTypeClass = null;
    public static String ASC = "ASC";//降序排序
    public static String DESC = "DESC";//升序排序

    /**
     * 对于已排序的集合,给出排列名次,对于相同名次的对象,下一个对象的名次是否连续
     * @param list        要进行给出排列名次的集合
     * @param objClazz    集合里的对象
     * @param _sortField  按某字段排序
     * @param _rankField  存储排列名次的字段,名字来源于实体类属性
     * @param continu     相同名次的对象,下一个对象的名次是否连续
     */
    public void sortRank(List<T> list, Class objClazz, String _sortField, String _rankField, boolean continu){

        if (continu) { // 1,2,3,4,5 这样连续排名
            int i = 1;
            for (T obj : list) {
                setValueByProperty(obj, _rankField, (long) i);
                i++;
            }
        }
        else          // 1,1,3,4,4,6 这样不连续排名
        {
            int i = 1;
            T pre = null;
            for (T obj : list) {

                if(i==1){
                    setValueByProperty(obj,_rankField,1L);
                }else{

                    if(getFieldValueByName(_sortField, obj).equals(getFieldValueByName(_sortField, pre))){//当前和前一名排名相同
                        setValueByProperty(obj,_rankField, getFieldValueByName(_rankField, pre));//记录前一个记录的排名
                        i++;
                        continue;
                    }
                    setValueByProperty(obj,_rankField,(long)i);

                }
                i++;
                pre = obj;

            }
        }

    }

    /**
     * 给出某个list集合, 对集合里的对象进行排序, 排序的字段和排序顺序可以配置
     * @param list        要进行排序的集合
     * @param _sortField  按某字段排序
     * @param objClazz    集合里的对象
     * @param _sortOrder  排序的顺序
     */
    public void sortGeneral(List<T> list, String _sortField, Class objClazz, String _sortOrder) {
        sortfield = _sortField;
        sortfieldTypeClass = getFiledType(_sortField, objClazz);
        sortOrder = _sortOrder;
        if (!sortfieldTypeClass.getName().equals("java.lang.Long") && !sortfieldTypeClass.getName().equals("java.lang.Double")) {
            throw new RuntimeException("排序字段类型要求是Long或者Double类型");
        }

        Comparator<T> comparator = new Comparator<T>() {
            public int compare(T s1, T s2) {

                if (getFieldValueByName(sortfield, s1) == null || getFieldValueByName(sortfield, s2) == null) {
                    throw new RuntimeException("比较字段的值不能为空");
                }

                // 控制排序顺序 start
                T first = null;
                T second = null;
                if (DESC.equals(sortOrder)) { // 从大到小排,3,2,1
                    first = s2;
                    second = s1;
                } else if (ASC.equals(sortOrder)) { // 从小到大排,1,2,3
                    first = s1;
                    second = s2;
                }
                // 控制排序顺序 end

                if (!getFieldValueByName(sortfield, first).equals(getFieldValueByName(sortfield, second))) {
                    if (("java.lang.Double").equals(sortfieldTypeClass.getName())) {
                        return Double.compare((Double) getFieldValueByName(sortfield, first), (Double) getFieldValueByName(sortfield, second));
                    }
                    return Double.compare((Long) getFieldValueByName(sortfield, first), (Long) getFieldValueByName(sortfield, second));
                } else {
                    return 0;
                }
            }
        };

        Collections.sort(list, comparator);

    }

    /**
     * 根据属性名获取属性值
     * */
    public Object getFieldValueByName(String fieldName, Object o) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter, new Class[] {});
            Object value = method.invoke(o, new Object[] {});
            return value;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据属性名获取属性类型
     * */
    public Class<?> getFiledType(String field, Class classParam) {
        Field[] fields = classParam.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if(fields[i].getName().equals(field)){
                return fields[i].getType();
            }
        }
        return null;
    }

    /**
     * 对象任意属性赋值
     * 对象要求有get,set方法
     */
    public Object setValueByProperty(Object objMain,String property,Object objValue) {
        try{

            Method[] arrayMethodsOut =objMain.getClass().getMethods();
            Map<String,Method> mapMethodsOut =new HashMap<String, Method>(); 
            for( int i=0;i<arrayMethodsOut.length;i++ ){
                mapMethodsOut.put(arrayMethodsOut[i].getName(), arrayMethodsOut[i]);
            }

            String sMethodNameSet = "set" + StringUtil.formatFirstUpper(property);
            Method methodTempSet =mapMethodsOut.get( sMethodNameSet );
            if( methodTempSet!=null ){
                try{
                    //避免系统的Set方法,影响其他属性
                    methodTempSet.invoke(objMain, new Object[]{objValue});
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {

        //构造测试例子start
        SortUtil<TspPefScoreCalTotal> testSort = new SortUtil<TspPefScoreCalTotal>();
        TspPefScoreCalTotal t1 = new TspPefScoreCalTotal();
        TspPefScoreCalTotal t2 = new TspPefScoreCalTotal();
        TspPefScoreCalTotal t3 = new TspPefScoreCalTotal();
        TspPefScoreCalTotal t4 = new TspPefScoreCalTotal();
        t1.setPefTotalScoreId(1L);
        t1.setBuyerCompanyId(1L);
        t1.setTotalScore(10L);
        t2.setPefTotalScoreId(2L);
        t2.setBuyerCompanyId(1L);
        t2.setTotalScore(5L);
        t3.setPefTotalScoreId(3L);
        t3.setBuyerCompanyId(1L);
        t3.setTotalScore(20L);
        t4.setPefTotalScoreId(4L);
        t4.setBuyerCompanyId(1L);
        t4.setTotalScore(20L);

        List<TspPefScoreCalTotal> list = new ArrayList<TspPefScoreCalTotal>();
        list.add(t1);
        list.add(t2);
        list.add(t3);
        list.add(t4);
        //构造测试例子end

        //对任意对象集合进行指定字段排序
        testSort.sortGeneral(list, "totalScore", TspPefScoreCalTotal.class, DESC);

        //对于上述已排序的集合,排列名次
        testSort.sortRank(list, TspPefScoreCalTotal.class, "totalScore", "totalScoreRanking", false);

        //输出结果
        for(Object t:list){
        System.out.println(((TspPefScoreCalTotal)t).getPefTotalScoreId() + "," + ((TspPefScoreCalTotal)t).getTotalScore()
                            + "," + ((TspPefScoreCalTotal)t).getTotalScoreRanking()
                );
        }
    }
}
public class TspPefScoreCalTotal {

private Long pefTotalScoreId; //主键ID
private Long totalScore; //得分
private Long totalScoreRanking; //排列名次

public Long getPefTotalScoreId() {
        return this.pefTotalScoreId;
    }
public void setPefTotalScoreId (Long pefTotalScoreId) {
        this.pefTotalScoreId = pefTotalScoreId;
    }

public Long getTotalScore() {
        return this.totalScore;
    }
public void setTotalScore (Long totalScore) {
        this.totalScore = totalScore;
    }
public Long getTotalScoreRanking() {
        return this.totalScoreRanking;
    }
public void setTotalScoreRanking (Long totalScoreRanking) {
        this.totalScoreRanking = totalScoreRanking;
    }   

}

当然,上面的程序还没有考虑对于大数据量时候的排序处理是否有性能问题。下回找个时间测试一下。

<think>我们使用Java的Stream API结合forEach来实现排序并写入序号字段。注意:由于排序操作需要整个集合参与,因此不能直接在forEach中完成排序,但可以先排序,然后利用Stream的forEach来分配序号。 步骤: 1. 对集合进行排序(使用Stream的sorted或者先使用Collections.sort) 2. 使用IntStream生成序号,然后遍历排序后的列表,为每个对象设置序号。 但是注意:相同值的对象应该具有相同的序号(并列排名),并且下一个不同值会跳过并列的数量(例如:1,2,2,4)。 我们可以这样实现: - 先对列表排序(升序或降序) - 然后使用一个变量来记录上一个对象排序字段值,以及一个变量记录当前序号(注意相同值的处理) 由于在forEach中我们需要索引,我们可以使用一个原子整数(AtomicInteger)来记录当前序号,并处理相同值的情况。但是注意,在forEach中我们无法直接使用外部的基本类型变量(因为需要final或等效final),所以我们可以使用一个数组或对象引用(或AtomicInteger)来记录当前序号,同时记录上一个值。 然而,处理相同值的情况需要知道上一个对象的值,所以我们可以使用一个外部变量来记录上一个对象的值,然后比较当前对象的值和上一个对象的值,如果相同则序号不变(并且记录相同值的个数,以便于下一个不同值跳过这些并列的个数?),但这里我们采用另一种方式:先排序,然后遍历。 具体思路: - 排序列表 - 初始化当前序号为1,上一个值为null,相同值计数为0(用于在遇到新值时跳过并列的个数) - 遍历排序后的列表: if (当前对象的值等于上一个值) { 则当前对象的序号 = 上一个对象的序号; 相同值计数加1(用于下一个新值跳过这些并列) } else { 当前序号 = 当前序号 + 相同值计数; // 跳过并列的个数 相同值计数 = 0; // 重置 当前对象的序号 = 当前序号; } 然后当前序号++(或者不++,因为我们用相同值计数来处理)?注意:这里我们实际上在遇到新值时,序号应该是上一个序号加上相同值计数(即并列的数量)然后加1?不对,应该是上一个序号+1?但是为了处理并列,我们实际上在遇到新值时,序号应该是上一个序号(上一个新值的序号)加上上一个新值后面相同值的个数(即相同值计数),然后加1?不对,我们重新设计: 另一种更常见的排名规则: 分数:100, 99, 99, 98 排名:1, 2, 2, 4 (称为竞赛排名,跳过3) 实现: 初始化:currentRank = 1, skip = 0 第一个:100 -> rank=1, 记录prev=100, skip=0 第二个:99 -> 与100不同,所以 currentRank = currentRank + skip + 1? 不对,应该是直接currentRank++,然后设置为2?但是这里不同,所以应该是2,然后skip重置为0?不对,我们遇到不同值,应该将当前排名增加上一个相同值组的个数(即skip),然后重置skip为0,然后设置当前排名为这个值,然后当前排名自增(为下一个做准备)?这样不对。 更好的做法: 我们用一个变量currentIndex表示当前应该分配的序号(从1开始),然后记录上一个值prevValue。 另外用一个变量countEqual记录当前相同值的个数(包括第一个,即遇到新值时,countEqual重置为0,然后这个新值就是第一个,然后遇到下一个相同的,countEqual加1)。 具体步骤: currentIndex = 1 prevValue = null countEqual = 0 // 记录当前相同值组的个数(从第一个新值开始,每遇到一个相同的就加1,遇到新值时,currentIndex增加countEqual,然后countEqual重置为0) 遍历每个元素(已排序): if (prevValue == null) { // 第一个元素 setSort(currentIndex); prevValue = currentValue; countEqual = 0; // 注意:第一个元素还没有相同值,所以countEqual为0 } else { if (currentValue等于prevValue) { countEqual++; // 相同值个数增加 setSort(currentIndex); // 这里不对,因为第一个相同值应该和上一个相同,而currentIndex还没有增加 } else { // 遇到新值 currentIndex = currentIndex + countEqual + 1; // 跳过所有相同值(countEqual个)然后加1?不对,应该是当前索引增加(上一个相同值组的个数,然后当前新值应该排在后面) // 实际上,新值的排名应该是上一个值的排名+上一个相同值组的个数(即跳过这些并列的)然后新值排名就是currentIndex setSort(currentIndex); countEqual = 0; // 重置 prevValue = currentValue; } } 这个逻辑不对,因为第一个99出现时,它应该被设置为2(因为前面有一个100,所以排名第2),然后第二个99出现时,它应该也是2,并且countEqual加1,然后当遇到98时,currentIndex = 2 + 1 + 1 = 4? 不对,应该是2(当前排名)加上相同值个数(1)得到3,然后98的排名就是3+1=4?不对,98应该是第4名。 我们重新设计排名规则(竞赛排名): 排名:1, 2, 2, 4 当处理第一个99时,它和100不同,所以排名是2(即currentIndex=2),然后设置给第一个99,然后countEqual=0,prevValue=99。 然后第二个99:它等于prevValue,所以countEqual=1(表示在99这个值上已经有一个并列了),然后第二个99的排名仍然是2(即currentIndex不变),然后设置排名为2。 然后遇到98:此时,currentIndex应该更新为:currentIndex + countEqual + 1 = 2+1+1=4,然后98的排名就是4。 所以算法: int currentRank = 1; // 当前要分配的排名 int skip = 0; // 上一个相同值组中除了第一个外还有多少个(即并列的个数,不包括第一个) Object prev = null; for (T item : sortedList) { Object currentValue = getValue(item); // 获取当前对象排序字段值 if (prev == null) { // 第一个元素 setSort(item, currentRank); prev = currentValue; } else { if (currentValue.equals(prev)) { // 与上一个相同,则排名不变,但skip加1(因为下一个新值需要跳过这个并列) setSort(item, currentRank); skip++; } else { // 遇到新值:当前排名 = 当前排名 + skip + 1 currentRank = currentRank + skip + 1; setSort(item, currentRank); skip = 0; // 重置skip prev = currentValue; } } } 注意:第一个元素之后,currentRank=1,skip=0,prev有值。 第二个元素(99): 与prev(100)不同:则currentRank = 1+0+1=2,然后设置当前元素排名为2,skip重置为0,prev=99。 第三个元素(99): 与prev(99)相同:设置排名为2(当前currentRank),skip变为1。 第四个元素(98): 与prev(99)不同:currentRank = 2+1+1=4,设置排名为4。 这样得到排名:1,2,2,4 符合要求。 但是注意:我们也可以不改变currentRank,而用另一个变量表示当前排名,比如: int rank = 1; int sameCount = 0; Object prev = null; for (int i=0; i<list.size(); i++) { T item = list.get(i); Object currentValue = getValue(item); if (prev == null) { setSort(item, rank); } else { if (prev.equals(currentValue)) { sameCount++; setSort(item, rank); } else { // 遇到新值,更新排名 rank = rank + sameCount + 1; setSort(item, rank); sameCount = 0; // 重置 } } prev = currentValue; } 但是这样第一个元素之后,sameCount=0,rank=1,然后第二个元素如果不同,则更新rank=1+0+1=2,然后设置第二个元素为2,sameCount重置0,prev更新为第二个元素的值。 第三个元素如果和第二个相同,sameCount++(变为1),然后设置排名为2(当前rank)?不对,因为第三个元素应该和第二个元素排名相同,但此时rank已经是2了,所以直接设置2,然后sameCount=1。 第四个元素不同,则更新rank=2+1+1=4,设置第四个元素为4。 这样也正确。 但是注意:我们这里使用遍历索引也可以,但使用forEach(比如list.forEach)需要能访问外部变量,且需要修改外部变量,所以我们可以使用一个数组或者一个原子类来保存状态。 然而,由于我们排序后需要按顺序处理,并且要记录状态,使用传统的for循环更简单。但用户要求使用forEach,我们可以使用一个原子整数来保存rank和sameCount,但是注意状态需要两个变量,所以我们可以用一个对象来保存状态。 使用forEach的代码: Collections.sort(list, comparator); // 先排序 class State { int rank = 1; int sameCount = 0; Object prev = null; } State state = new State(); list.forEach(item -> { Object currentValue = getValue(item); // 如何获取值?可以通过函数式接口 if (state.prev == null) { setSort(item, state.rank); state.prev = currentValue; } else { if (state.prev.equals(currentValue)) { state.sameCount++; setSort(item, state.rank); } else { state.rank = state.rank + state.sameCount + 1; setSort(item, state.rank); state.sameCount = 0; state.prev = currentValue; } } }); 但是这里有一个问题:在遇到新值的时候,我们更新了state.rank,然后下一个元素就会使用这个新的rank。这符合要求。 但是,我们如何获取对象的字段值?可以使用函数式接口,避免反射。 因此,我们设计一个方法,接受两个函数式接口:一个用于获取排序字段的值(类型为可比较的),另一个用于设置序号。 完整代码示例: 注意:我们使用Java 8的Stream和Lambda表达式。 方法定义: public static <T> void sortAndAssignRank(List<T> list, Comparator<? super T> comparator, Function<T, ? extends Comparable> keyExtractor, BiConsumer<T, Integer> rankSetter) { // 先排序 list.sort(comparator); class State { int rank = 1; int sameCount = 0; Object prev = null; } State state = new State(); list.forEach(item -> { Comparable currentValue = keyExtractor.apply(item); if (state.prev == null) { rankSetter.accept(item, state.rank); state.prev = currentValue; } else { if (state.prev.equals(currentValue)) { state.sameCount++; rankSetter.accept(item, state.rank); } else { state.rank = state.rank + state.sameCount + 1; rankSetter.accept(item, state.rank); state.sameCount = 0; state.prev = currentValue; } } }); } 但是,注意:排序字段可能是任意类型,我们使用Comparable,并且使用equals比较。但是注意:currentValue可能是null,所以需要处理null。 改进:在比较的时候,使用Objects.equals(state.prev, currentValue)来处理null。 另外,我们也可以不传入comparator,而是根据keyExtractor生成一个比较器,但这样会限制排序方式(升序降序?)。所以我们可以要求调用者传入比较器,这样更灵活。 使用示例: List<Employee> employees = ...; sortAndAssignRank(employees, Comparator.comparing(Employee::getScore).reversed(), // 按分数降序 Employee::getScore, Employee::setSort); 注意:这里我们假设Employee有setSort方法。 但是注意:我们的排名规则是竞赛排名(1,2,2,4)。如果用户需要其他排名规则(比如1,2,2,3),即密集排名,那么算法不同。 密集排名:相同值的排名相同,并且下一个排名连续(即并列之后紧接着下一个名次,不跳过数字)。例如:1,2,2,3 因此,我们可以提供不同的排名方法,或者增加一个参数来指定排名规则。 由于用户没有指定,我们按照竞赛排名实现。 下面给出完整示例代码(包含测试):</think># Java使用forEach实现对象集合排序并写入序号字段 下面是一个完整的Java示例,使用`forEach`方法对对象集合进行排序,并将排序序号写入对象的`sort`字段中: ```java import java.util.*; import java.util.concurrent.atomic.AtomicInteger; public class SortWithForEachDemo { public static void main(String[] args) { // 创建测试数据 List<Student> students = new ArrayList<>(); students.add(new Student("Alice", 85)); students.add(new Student("Bob", 92)); students.add(new Student("Charlie", 78)); students.add(new Student("David", 92)); // 相同分数测试 students.add(new Student("Eva", 85)); students.add(new Student("Frank", 90)); System.out.println("排序前:"); students.forEach(System.out::println); // 使用forEach实现排序和序号分配 sortAndAssignRankWithForEach(students, "score", false); System.out.println("\n排序(带序号):"); students.forEach(System.out::println); } /** * 使用forEach对集合排序并分配序号 * * @param list 对象集合 * @param fieldName 排序依据的字段名 * @param ascending 是否升序排序 */ public static void sortAndAssignRankWithForEach(List<?> list, String fieldName, boolean ascending) { // 1. 先对集合进行排序 list.sort(createComparator(fieldName, ascending)); // 2. 使用forEach分配序号(处理相同值情况) AtomicInteger rank = new AtomicInteger(1); AtomicInteger sameValueCount = new AtomicInteger(0); Object[] prevValue = {null}; // 使用数组包装以在lambda中修改 list.forEach(item -> { try { // 获取当前对象的字段值 Object currentValue = item.getClass() .getDeclaredField(fieldName) .get(item); // 检查是否与上一个值相同 if (prevValue[0] != null && prevValue[0].equals(currentValue)) { sameValueCount.incrementAndGet(); } else { // 遇到新值,更新序号并重置计数器 rank.set(rank.get() + sameValueCount.get()); sameValueCount.set(0); } // 设置序号字段 item.getClass() .getDeclaredField("sort") .set(item, rank.get()); prevValue[0] = currentValue; // 为下一个元素准备序号(除非是最后一个元素) if (list.indexOf(item) < list.size() - 1) { rank.incrementAndGet(); } } catch (Exception e) { throw new RuntimeException("处理字段时出错", e); } }); } /** * 创建基于字段的比较器 */ private static Comparator<Object> createComparator(String field, boolean ascending) { return (o1, o2) -> { try { // 使用反射获取字段值 Comparable<Object> val1 = (Comparable<Object>) o1.getClass().getDeclaredField(field).get(o1); Comparable<Object> val2 = (Comparable<Object>) o2.getClass().getDeclaredField(field).get(o2); // 处理空值 if (val1 == null && val2 == null) return 0; if (val1 == null) return ascending ? -1 : 1; if (val2 == null) return ascending ? 1 : -1; // 比较并返回结果 int result = val1.compareTo(val2); return ascending ? result : -result; } catch (Exception e) { throw new RuntimeException("比较对象时出错", e); } }; } } class Student { private String name; private Integer score; // 排序依据字段 private Integer sort; // 序号字段 public Student(String name, Integer score) { this.name = name; this.score = score; } // Getter和Setter public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } @Override public String toString() { return "Student{name='" + name + "', score=" + score + ", sort=" + sort + '}'; } } ``` ## 代码说明 ### 1. 核心实现逻辑 - **排序**:使用`list.sort()`方法对集合进行排序 - **序号分配**: - 使用`AtomicInteger`确保在lambda表达式中安全修改序号值 - 使用数组包装上一个值(`Object[] prevValue`)以在lambda中修改 - 处理相同值的情况(相同分数分配相同序号) ### 2. 处理相同值情况 当遇到相同排序值的对象时,会分配相同的序号: ``` 输入: [Bob:92, David:92, Alice:85, Eva:85, ...] 输出: Bob:92 -> 序号1 David:92 -> 序号1(与Bob相同) ... Alice:85 -> 序号3 Eva:85 -> 序号3(与Alice相同) ``` ### 3. 替代方案:使用函数式接口(避免反射) ```java @FunctionalInterface interface FieldAccessor<T, V extends Comparable<V>> { V getValue(T obj); } @FunctionalInterface interface RankSetter<T> { void setRank(T obj, int rank); } public static <T> void sortAndAssignRankWithForEach( List<T> list, FieldAccessor<T, ? extends Comparable> fieldAccessor, RankSetter<T> rankSetter, boolean ascending ) { // 排序 list.sort((o1, o2) -> { Comparable val1 = fieldAccessor.getValue(o1); Comparable val2 = fieldAccessor.getValue(o2); if (val1 == null && val2 == null) return 0; if (val1 == null) return ascending ? -1 : 1; if (val2 == null) return ascending ? 1 : -1; int result = val1.compareTo(val2); return ascending ? result : -result; }); // 分配序号 AtomicInteger rank = new AtomicInteger(1); AtomicInteger sameValueCount = new AtomicInteger(0); Object[] prevValue = {null}; list.forEach(item -> { Comparable currentValue = fieldAccessor.getValue(item); if (prevValue[0] != null && prevValue[0].equals(currentValue)) { sameValueCount.incrementAndGet(); } else { rank.set(rank.get() + sameValueCount.get()); sameValueCount.set(0); } rankSetter.setRank(item, rank.get()); prevValue[0] = currentValue; if (list.indexOf(item) < list.size() - 1) { rank.incrementAndGet(); } }); } // 使用示例 sortAndAssignRankWithForEach( students, Student::getScore, Student::setSort, false ); ``` ## 输出示例 ``` 排序前: Student{name='Alice', score=85, sort=null} Student{name='Bob', score=92, sort=null} Student{name='Charlie', score=78, sort=null} Student{name='David', score=92, sort=null} Student{name='Eva', score=85, sort=null} Student{name='Frank', score=90, sort=null} 排序(带序号): Student{name='Bob', score=92, sort=1} Student{name='David', score=92, sort=1} Student{name='Frank', score=90, sort=3} Student{name='Alice', score=85, sort=4} Student{name='Eva', score=85, sort=4} Student{name='Charlie', score=78, sort=6} ``` ## 应用场景 1. 学生成绩排名系统 2. 销售业绩排行榜 3. 比赛结果排名 4. 任何需要显示排名的数据列表 5. Excel报表导出时添加排名列[^4]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值