Java集合排序

1. 集合排序API

1.1 集合排序概述

        集合排序是指对一个集合中的元素按照特定规则进行重新排列,以使得集合中的元素按照预定义的顺序呈现。

        在集合排序中,通常需要定义一个比较规则,这个比较规则用于决定集合中的元素在排序后的顺序。元素之间的比较可以是数字的大小比较、字符串的字典序比较、对象的属性比较等。

        例如,将学生信息集合按照学生的学号排序,按照姓名的字典顺序排序,或者按照生日排序。

        在Java中实现集合排序的方式可以分为两大类:

        1、使用集合排序API。

        2、使用支持自动排序的集合。

  • 一些集合底层使用的数据结构支持自动排序,如红黑树结构

1.2 Collections.sort() 方法

        Collections是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序的sort方法。该方法的定义为:

void sort(List<T> list)

        该方法的作用是对集合元素进行自然排序(按照元素的由小至大的顺序)。

        编写代码,测试集合的排序实现。代码示意如下:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class ListSortDemo1 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        Random r = new Random(1);
        for (int i = 0; i < 10; i++) {
            list.add(r.nextInt(100));
        }
        // [85, 88, 47, 13, 54, 4, 34, 6, 78, 48]
        System.out.println(list);
        Collections.sort(list);
        // [4, 6, 13, 34, 47, 48, 54, 78, 85, 88]
        System.out.println(list);
    }
} 

1.3 Comparable接口

        在Java中,如果想对某个集合的元素进行排序,有一个前提条件:该集合中的元素必须是Comparable接口的实现类。

        Comparable是一个接口,用于定义其子类是可以比较的,该接口有一个用于比较大小的抽象方法:

        所有 Comparable 接口的实现类都需要重写 compareTo 方法来定义对象间的比较规则:

int compareTo(T t);  

        此方法使用当前对象与给定对象进行比较,并要求返回一个整数,这个整数不关心具体的值,而是关注取值范围:

  • 当返回值>0时,表示当前对象比参数给定的对象大
  • 当返回值<0时,表示当前对象比参数给定的对象小
  • 当返回值=0时,表示当前对象和参数给定的对象相等

        编写代码,定义类并实现Comparable接口;然后定义包含该对象的集合,测试其排序效果。代码示意如下:

public class Student implements Comparable<Student>{
    String String name;
    String int age;
    String double score;
    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    @Override
    public int compareTo(Student o) {
        // 按年龄的大小排序
        return this.age - o.age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

import java.util.*;
public class ListSortDemo2 {
    public static void main(String[] args) {
        Student s1 = new Student("Tom", 18, 88.5);
        Student s2 = new Student("Jerry", 16, 95);
        Student s3 = new Student("Lucy", 17, 100);
        System.out.println("s1 compareTo s2:" + s1.compareTo(s2));
        System.out.println("s2 compareTo s3:" + s2.compareTo(s3));
        List<Student> list = Arrays.asList(s1,s2,s3);
        // 排序
        Collections.sort(list);
        // 查看list中的元素
        for(Student s : list){
            System.out.println(s); // Jerry, Lucy, Tom
        }
    }
}

1.4 Comparator接口

        一旦Java类实现了 Comparable 接口,其比较逻辑就已经确定;如果希望在排序的操作中临时指定比较规则,可以通过声明 Comparator 接口的实现类来实现。

        Comparator 接口也用于定义比较逻辑,可用于在集合外部提供元素的比较逻辑。对比如下图所示:

        因此,实现了 Comparator 接口的类可以看作是定义了特定比较逻辑的比较器。

        Comparator接口的核心方法是compare方法,用于比较两个元素的大小:

int compare(T o1,T o2)

        实现compare方法的返回值要求:

  • 若o1>o2则返回值应>0
  • 若o1<o2则返回值应<0
  • 若o1=o2则返回值应为0

        接续上一个案例:使用Comparator接口,为sort() 方法指定其他比较规则并实现排序。代码示意如下:

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ListSortDemo3 {
    public static void main(String[] args) {
        Student s1 = new Student("Tom", 18, 88.5);
        Student s2 = new Student("Jerry", 16, 95);
        Student s3 = new Student("Lucy", 17, 100);
        List<Student> list = Arrays.asList(s1,s2,s3);
        // 排序 指定新的比较逻辑 按分数排序
        System.out.println("====> 按分数排序后的结果");
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return (int) Math.ceil(o1.score - o2.score);
            }
        });
        // 查看list中的元素
        for(Student s : list){
            System.out.println(s); // Tom, Jerry, Lucy
        }

        // 排序 指定新的比较逻辑 按分数降序排序
        System.out.println("====> 按分数降序排序后的结果");
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return (int) Math.ceil(o2.score - o1.score);
            }
        });
        // 查看list中的元素
        for(Student s : list){
            System.out.println(s); // Lucy, Jerry, Tom
        }

        // 排序 指定新的比较逻辑 按姓名排列
        System.out.println("====> 按姓名排序后的结果");
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                // String和包装类都实现了Comparable接口
                return o1.name.compareTo(o2.name);
            }
        });
        // 查看list中的元素
        for(Student s : list){
            System.out.println(s); // Jerry, Lucy, Tom
        }
    }
}

1.5 Comparator接口中的默认方法

        在Java 8之前,接口中只能定义抽象方法,也就是只能定义方法的签名,而没有具体的实现。

        Java 8引入了默认方法(Default Method)的概念,它是一种可以在接口中定义具体实现的方法。默认方法的引入使得Java的接口可以更好地支持类库的演化和功能的扩展。

        Comparator 接口在Java 8及以后的版本中引入了一些默认方法,这些方法提供了更多的灵活性和便利性。

        以下是Comparator 接口中常用的默认方法:

        1、reversed():该方法返回当前比较器的逆序比较器。它将原来的比较规则进行颠倒,使得升序排序变为降序排序,反之亦然。

        2、thenComparing(Comparator<? super T> other):该方法返回一个组合比较器,用于对两个比较规则进行联合排序。如果原始比较器认为两个元素相等,则使用传入的 other 比较器进一步比较。

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ListSortDemo4 {
    public static void main(String[] args) {
        Student s1 = new Student("Tom", 18, 88.5);
        Student s2 = new Student("Jerry", 16, 95);
        Student s3 = new Student("Lucy", 17, 100);
        Student s4 = new Student("Alice", 17, 96);
        List<Student> list = Arrays.asList(s1,s2,s3,s4);
        // 声明年龄升序比较器
        Comparator<Student> c1 = new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.age - o2.age;
            }
        };
        // 声明年龄降序比较器
        Comparator<Student> c2 = c1.reversed();
        // 测试效果
        System.out.println("====> 按年龄升序排序后的结果");
        Collections.sort(list, c1);
        printList(list);
        System.out.println("====> 按年龄降序排序后的结果");
        Collections.sort(list, c2);
        printList(list);
        // 声明分数升序比较器
        Comparator<Student> c3 = new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return (int) Math.ceil(o1.score - o2.score);
            }
        };
        // 组合比较器:先按年龄降序,年龄相同按分数升序
        Comparator<Student> c4 = c2.thenComparing(c3);
        // 测试效果
        System.out.println("====> 按年龄降序,年龄相同按分数升序排序后的结果");
        Collections.sort(list, c4);
        printList(list);
    }

    public static void printList(List<Student> list) {
        for(Student s : list){
            System.out.println(s);
        }
    }
}

2. 自动排序的集合

2.1 树数据结构

        树(Tree)是一种常见的非线性数据结构,它由一组节点(Node)和节点之间的连接关系(边,Edge)组成。树的结构类似于自然界中的树,由根节点、分支节点和叶子节点构成,分支节点连接多个子节点,而叶子节点没有子节点。

2.2 二叉搜索树

        二叉搜索树(Binary Search Tree, BST)满足以下条件:

        1、对于根节点,左子树中所有节点的值 < 根节点的值 < 右子树中所有节点的值

        2、任意节点的左、右子树也是二叉搜索树,即同样满足条件 1

        这个特性使得在二叉搜索树中进行查找操作非常高效。在理想的情况下,二叉搜索树是“平衡”的,这样就可以在logn轮循环内查找任意节点。

        BST的插入和删除操作也相对简单,但它没有强制性的自平衡机制,可能导致树的不平衡性,如果二叉树退化成链表,这时各种操作的时间复杂度也会退化为O(n)。

2.3 红黑树

        红黑树(Red-Black Tree)是一种自平衡的二叉搜索树,它在普通二叉搜索树的基础上添加了额外的规则来保持树的平衡。红黑树的命名源自于每个节点都有一个颜色属性,可以是红色或黑色。

        红黑树通过遵守以下五条规则来保持树的平衡性:

  • 每个节点要么是红色,要么是黑色
  • 根节点是黑色
  • 每个叶子节点(NIL节点)都是黑色
  • 如果一个节点是红色的,则其两个子节点必须都是黑色的
  • 从任意节点到其每个叶子节点的简单路径上,黑色节点的数量相同

        示意如下:

2.4 TreeMap

        在Java中,TreeMap 是一种实现了 SortedMap 接口的有序映射集合。它基于红黑树(Red-Black Tree)数据结构来实现,可以确保其中的元素按照键的自然顺序或者自定义比较器进行排序。TreeMap 提供了一系列方法来操作键值对,具有快速查找、插入和删除的特性。

        以下是 TreeMap 的一些特点和用法:

        1、键的有序性:TreeMap 中的键是有序的,这是因为它基于红黑树来实现。键的排序可以是键类型的自然顺序,或者通过传入的 Comparator 对象来定义。

        2、查找效率:由于红黑树是一种自平衡的二叉搜索树,TreeMap 中的查找、插入和删除操作的时间复杂度都是 O(log n),其中 n 是映射中键值对的数量。

        3、允许 null 键:TreeMap 允许 null 键,但要注意在自定义比较器中处理 null 键的情况,否则可能导致异常。

        TreeMap充分发挥了二叉搜索树的特点,为用户提供了一些与Key元素大小相关的方法。

        编写代码,测试TreeMap的使用。代码示意如下:

import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

public class TreeMapDemo {
    public static void main(String[] args) {
        TreeMap<Integer, String> treeMap = new TreeMap();
        treeMap.put(5,"Tom");
        treeMap.put(3,"Jerry");
        treeMap.put(9,"Lucy");
        treeMap.put(2,"Tony");
        // 遍历TreeMap
        for(Map.Entry<Integer, String> entry : treeMap.entrySet()){
            System.out.println("key: " + entry.getKey()+", value: " + entry.getValue());
        }
        System.out.println("------------");
        // 返回临近的高值键值对
        Map.Entry<Integer,String> entry1 = treeMap.higherEntry(8);
        System.out.println("higherEntry(8): "+entry1);
        // 返回临近的低值键值对
        Map.Entry<Integer,String> entry2 = treeMap.lowerEntry(8);
        System.out.println("lowerEntry(8): "+entry2);
        System.out.println("------------");
        // 获取子集 key在 from和to之间,默认包前不包后
        SortedMap<Integer,String> subMap1 = treeMap.subMap(3,5);
        System.out.println("subMap(3, 5): "+subMap1);
        // 可设置是否包含边界
        System.out.println("------------");
        System.out.println("subMap(3, 5) include: "+treeMap.subMap(3, true, 9, true));
        // 返回逆序的集合
        System.out.println("------------");
        System.out.println("desc: "+treeMap.descendingMap());
    }
}

2.5 TreeSet

        在Java中,TreeSet 是一种实现了 SortedSet 接口的有序集合。它底层使用一个TreeMap的Key来存储所有的元素。TreeSet 中不允许包含重复的元素。

        编写代码,测试TreeSet的使用。代码示意如下:

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        Employee e1 = new Employee("Tom",18);
        Employee e2 = new Employee("Jerry",16);
        Employee e3 = new Employee("Lucy",13);
        Employee e4 = new Employee("Tony",20);
        TreeSet<Employee> set1 = new TreeSet();
        set1.add(e1);
        set1.add(e2);
        set1.add(e3);
        set1.add(e4);
        System.out.println("set1: "+set1);
        Comparator<Employee> cpt = new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return o1.name.compareTo(o2.name);
            }
        };
        TreeSet<Employee> set2 = new TreeSet(cpt);
        set2.addAll(set1);
        System.out.println("set2: "+set2);
    }
}

class Employee implements Comparable<Employee> {
    String name;
    int age;
    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public int compareTo(Employee o) {
        System.out.println(this.age + " compareTo" + o.age );
        return this.age - o.age;
    }
    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

3. 总结

        1、集合排序是指对一个集合中的元素按照特定规则进行重新排列,以使得集合中的元素按照预定义的顺序呈现。在Java中实现集合排序的方式可以分为两大类。

  • 使用集合排序API
  • 使用支持自动排序的集合

        2、常用的集合排序API包括以下几种。

  • Collections.sort() 方法
  • Comparable接口
  • Comparator接口

        3、TreeMap 是一种实现了 SortedMap 接口的有序映射集合。它基于红黑树(Red-Black Tree)数据结构来实现,可以确保其中的元素按照键的自然顺序或者自定义比较器进行排序。

        4、TreeSet 是一种实现了 SortedSet 接口的有序集合。它底层使用一个TreeMap的Key来存储所有的元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/593228.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

KIE基于图模型的关键信息抽取源码详解

1.数据集准备 下载数据集 https://download.openmmlab.com/mmocr/data/wildreceipt.tar WildReceiptOpenset 准备好 WildReceipt。 转换 WildReceipt 成 OpenSet 格式: # 你可以运行以下命令以获取更多可用参数: # python tools/dataset_converters/kie/closeset_to_opens…

程序的机器级表示——Intel x86 汇编讲解

往期地址&#xff1a; 操作系统系列一 —— 操作系统概述操作系统系列二 —— 进程操作系统系列三 —— 编译与链接关系操作系统系列四 —— 栈与函数调用关系操作系统系列五 —— 目标文件详解操作系统系列六 —— 详细解释【静态链接】操作系统系列七 —— 装载操作系统系列…

java下乡扶贫志愿者招募管理系统springboot-vue

计算机技术在现代管理中的应用&#xff0c;使计算机成为人们应用现代技术的重要工具。能够有效的解决获取信息便捷化、全面化的问题&#xff0c;提高效率。 技术栈 前端&#xff1a;vue.jsElementUI 开发工具&#xff1a;IDEA 或者eclipse都支持 编程语言: java 框架&#xff1…

c++ 红黑树学习及简单实现

1. 了解红黑树 1.1. 概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个节点增加一个存储位表示节点的颜色&#xff0c;可以是红色&#xff0c;或是黑色&#xff0c;通过对任何一条从根到叶子的路径上各个节点的着色方式进行限制&#xff0c;红黑树确保没有一条路…

Dockerfile镜像实例

目录 一、构建SSH镜像 1. 建立工作目录 2. 生成镜像 3. 启动容器并修改root密码 二、systemctl镜像 1. 建立工作目录 2. 生成镜像 3. 运行镜像容器 ​编辑 4. 测试容器systemct 三、Nginx镜像 1. 建立工作目录 2. 编写Dockerfile脚本 3. 编写run.sh启动脚本 4. …

IDEA启动Tomcat启动失败:jar包未部署【部署jar包】

IDEA启动Tomcat报错java.lang.ClassNotFoundException:org.springframework.web.context.ContextLoaderListener&#xff1a;jar包未部署【部署jar包】 学习java&#xff0c;开始跟着教程的步伐学习maven下载jar包&#xff0c;tomcat启动项目&#xff0c;发现项目未启动成功也…

虾皮(Shopee)商品详情API接口:轻松获取商品深度信息

API接口概述 虾皮的商品详情API接口是专为商家和开发者提供的服务接口&#xff0c;通过该接口&#xff0c;您可以快速、准确地获取指定商品的详细信息。这些信息包括但不限于商品标题、价格、库存、描述、图片、规格参数等&#xff0c;为您的商品展示、比价、推荐等场景提供有…

C++设计模式-结构型设计模式

写少量的代码来应对未来需求的变化。 单例模式 定义 保证一个类仅有一个实例&#xff0c;并提供一个该实例的全局访问点。——《设计模式》GoF 解决问题 稳定点&#xff1a; 类只有一个实例&#xff0c;提供全局的访问点&#xff08;抽象&#xff09; 变化点&#xff1a…

SpringCloud微服务:Eureka 和 Nacos 注册中心

共同点 都支持服务注册和服务拉取都支持服务提供者心跳方式做健康检测 不同点 Nacos 支持服务端主动检测提供者状态&#xff1a;临时实例采用心跳模式&#xff0c;非临时&#xff08;永久&#xff09;实例采用主动检测模式Nacos 临时实例心跳不正常会被剔除&#xff0c;非临时实…

【uniapp】H5+、APP模拟浏览器环境内部打开网页

前言 今天将智能体嵌入到我的项目中&#xff0c;当作app应用时&#xff0c;发现我使用的webview组件&#xff0c;无论H5怎么登录都是未登录&#xff0c;而APP却可以&#xff0c;于是进行了测试&#xff0c;发现以下几种情况&#xff1a; 方法<a>标签webviewAPP✅✅网页…

YOLOv5改进之bifpn

目录 一、原理 二、代码 三、在YOLOv5中的应用 一、原理 论文链接:

课题学习(二十三)---三轴MEMS加速度计芯片ADXL372

声明&#xff1a;本人水平有限&#xff0c;博客可能存在部分错误的地方&#xff0c;请广大读者谅解并向本人反馈错误。 一、基础配置 测量范围-200g-200g&#xff0c;分辨率为12位&#xff0c; V s 、 V D D I / O V_s、V_{DDI/O} Vs​、VDDI/O​范围为1.6V-3.5V 1.1 引脚配…

【银角大王——Django课程——用户表的基本操作2】

用户表的基本操作2 编辑用户按钮删除按钮入职日期——不显示时分&#xff0c;只显示年月日——使用DataField函数不使用DateTimeField修改models记得重新执行命令&#xff0c;更新数据库结构修改前修改后 编辑用户按钮 点击编辑&#xff0c;跳转到编辑页面&#xff08;将编辑的…

CrossOver支持的软件多吗 CrossOver支持软件列表 crossover兼容性查询

如果你是一个喜欢在Mac上工作的用户&#xff0c;但又不想放弃一些Windows上的优秀软件&#xff0c;那么可以考虑使用一些兼容工具来运行Windows程序。其中&#xff0c;CrossOver就是一款功能强大且受欢迎的兼容工具。那么&#xff0c;CrossOver到底能支持哪些Windows软件呢&…

JVM笔记2--垃圾收集算法

1、如何确认哪些对象“已死” 在上一篇文章中介绍到Java内存运行时的各个区域。其中程序计数器、虚拟机栈、本地方法栈3个区域随着线程而生&#xff0c;随线程而灭&#xff0c;栈中的栈帧随着方法的进入和退出而有条不紊的执行着入栈和出栈操作。每个栈帧中分配多少内存基本上…

VMvare如何更改虚拟机内共享文件夹的挂载点

更改虚拟机内共享文件夹的路径 进入目录 /etc/init.d ,并找到vmware-tools文件 里面有配置项 vmhgfs_mnt"/mnt/hgfs" 将引号内的内容更改为你需要挂载的路径,重启即可 注意挂载的路径不能是 “/”&#xff0c;必须根目录下的某个文件夹&#xff0c;或者其子文件夹 …

定时器编程前配置和控制LED隔一秒亮灭

1.配置定时器 0 工作模式16位计时 2.给初值&#xff0c;定一个10ms出来 3.开始计时

环形链表的判断方法与原理证明

&#xff08;题目来源&#xff1a;力扣&#xff09; 一.判读一个链表是否是环形链表 题目&#xff1a; 解答&#xff1a; 方法&#xff1a;快慢指针法 内容&#xff1a;分别定义快慢指针&#xff08;fast和slow&#xff09;&#xff0c;快指针一次走两步&#xff0c;慢指…

物体检测:如何检测小物体?

原文地址&#xff1a;https://medium.com/voxel51/how-to-detect-small-objects-cfa569b4d5bd 2024 年 4 月 22 日 物体检测是计算机视觉的基本任务之一。在高层次上&#xff0c;它涉及预测图像中物体的位置和类别。最先进的&#xff08;SOTA&#xff09;深度学习模型&#x…

3031087 -“无数据”:物料不显示在 MRP 应用中

症状 使用其中一个 MRP 应用&#xff08;监控物料覆盖范围、管理物料覆盖范围、监控外部需求等&#xff09;时无法找到物料。 用户在搜索过滤器时会收到错误消息“无数据”。 “本 KBA 中的图像/数据来自 SAP 内部系统、示例数据或演示系统。任何与真实数据相似的都是完全巧…
最新文章