当前位置: 代码网 > it编程>编程语言>Java > Java使用Lambda表达式实现排序功能的实现代码

Java使用Lambda表达式实现排序功能的实现代码

2025年07月18日 Java 我要评论
基于comparator排序在 java8 之前,都是通过实现comparator接口完成排序,比如:new comparator<student>() { @override

基于comparator排序

在 java8 之前,都是通过实现comparator接口完成排序,比如:

new comparator<student>() {
    @override
    public int compare(student h1, student h2) {
        return h1.getname().compareto(h2.getname());
    }
};

这里展示的是匿名内部类的定义,如果是通用的对比逻辑,可以直接定义一个实现类。使用起来也比较简单,如下就是应用:

@test
void basesortedorigin() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12)
    );
    collections.sort(students, new comparator<student>() {
        @override
        public int compare(student h1, student h2) {
            return h1.getname().compareto(h2.getname());
        }
    });
    assertions.assertequals(students.get(0), new student("jerry", 12));
}

这里使用了 junit5 实现单元测试,用来验证逻辑非常适合。
因为定义的comparator是使用name字段排序,在 java 中,string类型的排序是通过单字符的 ascii 码顺序判断的,j排在t的前面,所以jerry排在第一个。 

使用 lambda 表达式替换comparator匿名内部类

使用过 java8 的 lamdba 的应该知道,匿名内部类可以简化为 lambda 表达式为:

collections.sort(students, (student h1, student h2) -> h1.getname().compareto(h2.getname()));

在 java8 中,list类中增加了sort方法,所以collections.sort可以直接替换为:

students.sort((student h1, student h2) -> h1.getname().compareto(h2.getname()));

根据 java8 中 lambda 的类型推断,可以将指定的student类型简写:

students.sort((h1, h2) -> h1.getname().compareto(h2.getname()));

至此,整段排序逻辑可以简化为:

@test
void basesortedlambdawithinferring() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12)
    );
    students.sort((h1, h2) -> h1.getname().compareto(h2.getname()));
    assertions.assertequals(students.get(0), new student("jerry", 12));
}

通过静态方法抽取公共的 lambda 表达式

可以在student中定义一个静态方法:

public static int comparebynamethenage(student s1, student s2) {
    if (s1.name.equals(s2.name)) {
        return integer.compare(s1.age, s2.age);
    } else {
        return s1.name.compareto(s2.name);
    }
}

这个方法需要返回一个int类型参数,在 java8 中,可以在 lambda 中使用该方法:

@test
void sortedusingstaticmethod() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12)
    );
    students.sort(student::comparebynamethenage);
    assertions.assertequals(students.get(0), new student("jerry", 12));
}

借助comparator的comparing方法

在 java8 中,comparator类新增了comparing方法,可以将传递的function参数作为比较元素,比如:

@test
void sortedusingcomparator() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12)
    );
    students.sort(comparator.comparing(student::getname));
    assertions.assertequals(students.get(0), new student("jerry", 12));
}

多条件排序

在静态方法一节中展示了多条件排序,还可以在comparator匿名内部类中实现多条件逻辑:

@test
void sortedmulticondition() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12),
            new student("jerry", 13)
    );
    students.sort((s1, s2) -> {
        if (s1.getname().equals(s2.getname())) {
            return integer.compare(s1.getage(), s2.getage());
        } else {
            return s1.getname().compareto(s2.getname());
        }
    });
    assertions.assertequals(students.get(0), new student("jerry", 12));
}

从逻辑来看,多条件排序就是先判断第一级条件,如果相等,再判断第二级条件,依次类推。在 java8 中可以使用comparing和一系列thencomparing表示多级条件判断,上面的逻辑可以简化为:

@test
void sortedmulticonditionusingcomparator() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12),
            new student("jerry", 13)
    );
    students.sort(comparator.comparing(student::getname).thencomparing(student::getage));
    assertions.assertequals(students.get(0), new student("jerry", 12));
}

这里的thencomparing方法是可以有多个的,用于表示多级条件判断,这也是函数式编程的方便之处。 

在stream中进行排序

java8 中,不但引入了 lambda 表达式,还引入了一个全新的流式 api:stream api,其中也有sorted方法用于流式计算时排序元素,可以传入comparator实现排序逻辑:

@test
void streamsorted() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12)
    );
    final comparator<student> comparator = (h1, h2) -> h1.getname().compareto(h2.getname());
    final list<student> sortedstudents = students.stream()
            .sorted(comparator)
            .collect(collectors.tolist());
    assertions.assertequals(sortedstudents.get(0), new student("jerry", 12));
}

同样的,可以通过 lambda 简化书写:

@test
void streamsortedusingcomparator() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12)
    );
    final comparator<student> comparator = comparator.comparing(student::getname);
    final list<student> sortedstudents = students.stream()
            .sorted(comparator)
            .collect(collectors.tolist());
    assertions.assertequals(sortedstudents.get(0), new student("jerry", 12));
}

倒序排列

调转排序判断

排序就是根据compareto方法返回的值判断顺序,如果想要倒序排列,只要将返回值取返即可:

@test
void sortedreverseusingcomparator2() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12)
    );
    final comparator<student> comparator = (h1, h2) -> h2.getname().compareto(h1.getname());
    students.sort(comparator);
    assertions.assertequals(students.get(0), new student("tom", 10));
}

可以看到,正序排列的时候,是h1.getname().compareto(h2.getname()),这里直接倒转过来,使用的是h2.getname().compareto(h1.getname()),也就达到了取反的效果。在 java 的collections中定义了一个java.util.collections.reversecomparator内部私有类,就是通过这种方式实现元素反转。
借助**comparator****reversed**方法倒序
在 java8 中新增了reversed方法实现倒序排列,用起来也是很简单:

@test
void sortedreverseusingcomparator() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12)
    );
    final comparator<student> comparator = (h1, h2) -> h1.getname().compareto(h2.getname());
    students.sort(comparator.reversed());
    assertions.assertequals(students.get(0), new student("tom", 10));
}

在comparator.comparing中定义排序反转

import com.google.common.collect.lists;
import org.junit.jupiter.api.test;

import java.util.comparator;
import java.util.list;

import static org.junit.jupiter.api.assertions.assertequals;

class student {
    private string name;
    private int age;

    public student(string name, int age) {
        this.name = name;
        this.age = age;
    }

    public string getname() {
        return name;
    }

    public int getage() {
        return age;
    }

    @override
    public boolean equals(object o) {
        if (this == o) return true;
        if (o == null || getclass() != o.getclass()) return false;
        student student = (student) o;
        return age == student.age && name.equals(student.name);
    }

    @override
    public int hashcode() {
        return name.hashcode() + age;
    }

    @override
    public string tostring() {
        return "student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class comparatortest {

    @test
    void sortedusingcomparatorreverse() {
        final list<student> students = lists.newarraylist(
                new student("tom", 10),
                new student("jerry", 12)
        );

        // 使用comparator.comparing的重载方法,传入comparator.reverseorder()实现倒序
        students.sort(comparator.comparing(student::getname, comparator.reverseorder()));

        // 验证排序结果
        assertequals(students.get(0), new student("jerry", 12));
    }
}

在stream中定义排序反转

stream中的操作与直接列表排序类似,可以反转comparator定义,也可以使用comparator.reverseorder()反转。实现如下:

@test
void streamreversesorted() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12)
    );
    final comparator<student> comparator = (h1, h2) -> h2.getname().compareto(h1.getname());
    final list<student> sortedstudents = students.stream()
            .sorted(comparator)
            .collect(collectors.tolist());
    assertions.assertequals(sortedstudents.get(0), new student("tom", 10));
}
@test
void streamreversesortedusingcomparator() {
    final list<student> students = lists.newarraylist(
            new student("tom", 10),
            new student("jerry", 12)
    );
    final list<student> sortedstudents = students.stream()
            .sorted(comparator.comparing(student::getname, comparator.reverseorder()))
            .collect(collectors.tolist());
    assertions.assertequals(sortedstudents.get(0), new student("tom", 10));
}

null 值的判断

前面的例子中都是有值元素排序,能够覆盖大部分场景,但有时候还是会碰到元素中存在null的情况:

  1. 列表中的元素是 null
  2. 列表中的元素参与排序条件的字段是 null

如果还是使用前面的那些实现会碰到nullpointexception异常,即 npe,简单演示一下:

@test
void sortednullgotnpe() {
    final list<student> students = lists.newarraylist(
            null,
            new student("snoopy", 12),
            null
    );
    assertions.assertthrows(nullpointerexception.class,
            () -> students.sort(comparator.comparing(student::getname)));
}

所以,需要考虑这些场景。 

元素是 null 的笨拙实现

最先想到的就是判空:

@test
void sortednullnonpe() {
    final list<student> students = lists.newarraylist(
            null,
            new student("snoopy", 12),
            null
    );
    students.sort((s1, s2) -> {
        if (s1 == null) {
            return s2 == null ? 0 : 1;
        } else if (s2 == null) {
            return -1;
        }
        return s1.getname().compareto(s2.getname());
    });
    assertions.assertnotnull(students.get(0));
    assertions.assertnull(students.get(1));
    assertions.assertnull(students.get(2));
}

可以将判空的逻辑抽取出一个comparator,通过组合方式实现:

class nullcomparator<t> implements comparator<t> {
    private final comparator<t> real;
    nullcomparator(comparator<? super t> real) {
        this.real = (comparator<t>) real;
    }
    @override
    public int compare(t a, t b) {
        if (a == null) {
            return (b == null) ? 0 : 1;
        } else if (b == null) {
            return -1;
        } else {
            return (real == null) ? 0 : real.compare(a, b);
        }
    }
}

在 java8 中已经准备了这个实现。 

使用comparator.nullslast和comparator.nullsfirst

使用comparator.nullslast实现null在结尾:

@test
void sortednulllast() {
    final list<student> students = lists.newarraylist(
            null,
            new student("snoopy", 12),
            null
    );
    students.sort(comparator.nullslast(comparator.comparing(student::getname)));
    assertions.assertnotnull(students.get(0));
    assertions.assertnull(students.get(1));
    assertions.assertnull(students.get(2));
}

使用comparator.nullsfirst实现null在开头:

@test
void sortednullfirst() {
    final list<student> students = lists.newarraylist(
            null,
            new student("snoopy", 12),
            null
    );
    students.sort(comparator.nullsfirst(comparator.comparing(student::getname)));
    assertions.assertnull(students.get(0));
    assertions.assertnull(students.get(1));
    assertions.assertnotnull(students.get(2));
}

是不是很简单,接下来看下如何实现排序条件的字段是 null 的逻辑。 

排序条件的字段是 null

这个就是借助comparator的组合了,就像是套娃实现了,需要使用两次comparator.nullslast,这里列出实现:

@test
void sortednullfieldlast() {
    final list<student> students = lists.newarraylist(
            new student(null, 10),
            new student("snoopy", 12),
            null
    );
    final comparator<student> nullslast = comparator.nullslast(
            comparator.nullslast( // 1
                    comparator.comparing(
                            student::getname,
                            comparator.nullslast( // 2
                                    comparator.naturalorder() // 3
                            )
                    )
            )
    );
    students.sort(nullslast);
    assertions.assertequals(students.get(0), new student("snoopy", 12));
    assertions.assertequals(students.get(1), new student(null, 10));
    assertions.assertnull(students.get(2));
}

代码逻辑如下:

  1. 代码 1 是第一层 null-safe 逻辑,用于判断元素是否为 null;
  2. 代码 2 是第二层 null-safe 逻辑,用于判断元素的条件字段是否为 null;
  3. 代码 3 是条件comparator,这里使用了comparator.naturalorder(),是因为使用了string排序,也可以写为string::compareto。如果是复杂判断,可以定义一个更加复杂的comparator,组合模式就是这么好用,一层不够再套一层。

以上就是java使用lambda表达式实现排序功能的实现代码的详细内容,更多关于java lambda排序功能的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com