0
点赞
收藏
分享

微信扫一扫

[Java]Java中的排序原来可以这么玩

勇敢乌龟 2021-12-20 阅读 178

1. Java中的排序算法

讲Java中的集合排序前,我们来一起思考一下: 什么样的排序算法适合做为标准类库的算法?

  • 对比各种常见排序算法的优劣性
算法 稳定性 时间复杂度 空间复杂度
选择排序 不稳定 1
希尔排序 不稳定 说法很多,N^(1.25-2) 1
堆排序 不稳定 NlogN 1
快速排序 不稳定 NlogN lgN
插入排序 稳定 N-N² 1
归并排序 稳定 NlogN N

其中,快速排序算法对于多数的情况都是效率较高的,其内循环中的指令很少并且总是顺序地访问数据,可以利用缓存(预加载),但是稳定性无法保证(如果待排序的元素序列本身即为有序的状态).
如果对稳定性有要求,那么归并排序可能是更为适合的,它的弊端是需要更多的空间(通常编写归并排序,我们需要创建临时数组).
关于局部性原理知识,可以通往下面链接看知乎大神如何讨论:
如何理解计算机操作系统中的局部性原理?
关于快速排序算法的讨论:
快速排序到底有多快
排序算法(三): Quick Sort 快速排序(包含三向切分)

  • 原始类型数据与引用类型数据

为此,Java中对 原始类型数组(类型为 int、long、short、char、byte、boolean、float 或 double 的数组) 采用优化的快速排序算法-DualPivotQuicksort.
对于引用类型数据的集合采用基于归并排序的TimSort算法.

2. 使用集合排序需要注意的问题

禁止对不可变列表(unmodifiableList)进行排序,传入的列表可以为必须是可修改的, 但不必是可以改变大小的.

  • 错误示范
Student xiaoMing = Student.builder().age(20).name("20岁的小明").build();
Student xiaoHong = Student.builder().age(22).name("22岁的小红").build();
Student xiaoHua = Student.builder().age(25).name("25岁的小华").build();
List<Student> studentList = Collections.unmodifiableList(Lists.newArrayList(xiaoHong, xiaoMing, xiaoHua));
// 使用不可变集合,会抛出UnsupportedOperationException异常
Collections.sort(studentList);
studentList.forEach(e -> System.out.print(JacksonUtils.toJsonString(e) + "\n"));

3. 实战Java排序

3.1 数据源准备

  • 业务对象-Student
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * @author jaymin
 */
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student implements Serializable, Comparable {
    /**
     * 年龄
     */
    private Integer age;
    /**
     * 名字
     */
    private String name;

    /**
     * the value 0 if x == y; a value less than 0 if x < y; and a value greater than 0 if x > y
     * @param o 与之比较的其他学生
     * @return
     */
    @Override
    public int compareTo(Object o) {
        Student otherStudent = (Student) o;
        return Integer.compare(this.age, otherStudent.age);
    }
}
  • 声明成员变量
import com.google.common.collect.Lists;
import com.tea.modules.model.Student;
import com.tea.modules.util.JacksonUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @author jaymin.<br>
 * 在一次使用lambda的时候对Java排序产生了兴趣。因此本demo用来研究集合排序.<br>
 * 2021/2/18 20:16
 */
@SuppressWarnings("unchecked")
public class CollectionSort {
    /**
     * 数字列表
     */
    protected final static List<Integer> numberList = Lists.newArrayList(2, 3, 4, 1, 5, 2, 5, 6, 7, 99);
    /**
     * 存储不同年龄段的学生列表
     */
    protected static List<Student> studentList;

    /**
     * 构建出学生列表参数
     */
    private static void createStudentList() {
        Student xiaoMing = Student.builder().age(20).name("20岁的小明").build();
        Student xiaoHong = Student.builder().age(22).name("22岁的小红").build();
        Student xiaoHua = Student.builder().age(25).name("25岁的小华").build();
        studentList = Lists.newArrayList(xiaoHong, xiaoMing, xiaoHua);
    }
}

3.2 对原始类型列表进行排序

    /**
     * 使用Collections.sort对集合进行升序排序.<br>
     * 要求集合中的对象实现Comparable接口.<br>
     */
    public static void sortByCollections() {
        Collections.sort(numberList);
        numberList.forEach(e -> System.out.print(e + "-"));
    }
  • Result
1-2-2-3-4-5-5-6-7-99-

3.3 对实现了Comparable接口的对象进行排序

    /**
     * 这里我们使用了集合存储学生的信息.<br>
     * 如果此时希望按照年龄来进行排序,可以在Student类中实现Comparable接口。<br>
     * 然后再使用Collections.sort进行排序.<br>
     */
    public static void sortByCollectionsComparable() {
        createStudentList();
        Collections.sort(studentList);
        studentList.forEach(e -> System.out.print(JacksonUtils.toJsonString(e) + "\n"));
    }
  • Result
{
  "age" : 20,
  "name" : "20岁的小明"
}
{
  "age" : 22,
  "name" : "22岁的小红"
}
{
  "age" : 25,
  "name" : "25岁的小华"
}

3.4 使用比较器-Comparator来进行排序

    /**
     * 不实现接口,使用比较器-Comparator来进行排序.<br>
     * 比较器接收两个参数,可以理解成,给定两个学生对象,你实现compare方法来规定按什么顺序进行排序.<br>
     * 比较规则:the value 0 if x == y; a value less than 0 if x < y; and a value greater than 0 if x > y.<br>
     * 首先我们通过匿名内部类来完成这项工作.<br>
     *
     * @see Comparator
     */
    public static void sortByCollectionsComparator() {
        createStudentList();
        Collections.sort(studentList, new Comparator<Student>() {
            @Override
            public int compare(Student studentA, Student studentB) {
                return Integer.compare(studentA.getAge(), studentB.getAge());
            }
        });
        studentList.forEach(e -> System.out.print(JacksonUtils.toJsonString(e) + "\n"));
    }

3.5 Java8中使用lambda来简化匿名内部类.

    /**
     * 在Java8中,你可以使用lambda来简化匿名内部类.<br>
     */
    public static void sortByCollectionsComparatorInJava8() {
        createStudentList();
        // 常规的lambda
        Collections.sort(studentList, (studentA, studentB) -> Integer.compare(studentA.getAge(), studentB.getAge()));
        // 同时,你可以将这种方式改写成方法引用
        Collections.sort(studentList, Comparator.comparingInt(Student::getAge));
        studentList.forEach(e -> System.out.print(JacksonUtils.toJsonString(e) + "\n"));
    }

3.6 使用Java8的stream进行排序

    /**
     * 使用Java8的stream流,让你的集合操作更多元化.<br>
     */
    public static void sortByStream() {
        createStudentList();
        // 升序
        List<Student> result = studentList.stream()
                .sorted(Comparator.comparing(Student::getAge))
                .collect(Collectors.toList());
        result.forEach(e -> System.out.print(JacksonUtils.toJsonString(e) + "\n"));
        System.out.println("------------reversedOrder---------------");
        // 倒序
        List<Student> reversedOrderResult = studentList.stream()
                .sorted(Comparator.comparingInt(Student::getAge).reversed())
                .collect(Collectors.toList());
        reversedOrderResult.forEach(e -> System.out.print(JacksonUtils.toJsonString(e) + "\n"));
    }
  • Result
{
  "age" : 20,
  "name" : "20岁的小明"
}
{
  "age" : 22,
  "name" : "22岁的小红"
}
{
  "age" : 25,
  "name" : "25岁的小华"
}
------------reversedOrder---------------
{
  "age" : 25,
  "name" : "25岁的小华"
}
{
  "age" : 22,
  "name" : "22岁的小红"
}
{
  "age" : 20,
  "name" : "20岁的小明"
}
  • 空指针保护的Comparator.comparing
  • 触发空指针异常
    /**
     * 使用null值保护比较器来避免空指针异常<br>
     */
    public static void sortByStreamWithNullFirst() {
        createStudentList();
        studentList.get(0).setAge(null);
        // 升序
        List<Student> result = studentList.stream()
                .sorted(Comparator.comparing(Student::getAge))
                .collect(Collectors.toList());
        result.forEach(e -> System.out.print(JacksonUtils.toJsonString(e) + "\n"));
    }
  • 使用nullFirst和nullLast来避免空指针异常
    /**
     * 使用null值保护比较器来避免空指针异常<br>
     */
    public static void sortByStreamWithNullFirst() {
        createStudentList();
        studentList.get(0).setAge(null);
        // 升序,空值排在第一位
        List<Student> result = studentList.stream()
                .sorted(Comparator.comparing(Student::getAge,Comparator.nullsFirst(Comparator.naturalOrder())))
                .collect(Collectors.toList());
        result.forEach(e -> System.out.print(JacksonUtils.toJsonString(e) + "\n"));
        System.out.println("******************** Null First reverseOrderResult*****************************");
        // 降序,空值排在第一位
        List<Student> reverseOrderResult = studentList.stream()
                .sorted(Comparator.comparing(Student::getAge,Comparator.nullsFirst(Comparator.reverseOrder())))
                .collect(Collectors.toList());
        reverseOrderResult.forEach(e -> System.out.print(JacksonUtils.toJsonString(e) + "\n"));
        System.out.println("******************** Null Last reverseOrderResult*****************************");
        // 降序,空值排在第一位
        List<Student> nullLastResult = studentList.stream()
                .sorted(Comparator.comparing(Student::getAge,Comparator.nullsLast(Comparator.reverseOrder())))
                .collect(Collectors.toList());
        nullLastResult.forEach(e -> System.out.print(JacksonUtils.toJsonString(e) + "\n"));
    }
  • Result
{
  "name" : "22岁的小红"
}
{
  "age" : 20,
  "name" : "20岁的小明"
}
{
  "age" : 25,
  "name" : "25岁的小华"
}
******************** Null First reverseOrderResult*****************************
{
  "name" : "22岁的小红"
}
{
  "age" : 25,
  "name" : "25岁的小华"
}
{
  "age" : 20,
  "name" : "20岁的小明"
}
******************** Null Last reverseOrderResult*****************************
{
  "age" : 25,
  "name" : "25岁的小华"
}
{
  "age" : 20,
  "name" : "20岁的小明"
}
{
  "name" : "22岁的小红"
}

源码地址

所有源码已经上传到我的个人仓库,有兴趣的可以pull下来进行demo.
仓库地址
代码引用地址:com.tea.modules.java8.collection.sort.CollectionSort

举报

相关推荐

0 条评论