集合专题----set篇

news2024/11/15 23:38:29

1、Set 接口和常用方法

(1)Set 接口基本介绍

① 无序(添加和去除的顺序不一致),没有索引;

② 不允许重复元素,所以最多包含一个null;

(2)Set 接口的常用方法

和 List 接口一样, Set 接口也是 Collection 的子接口,因此,常用方法和 Collection 接口一样。

(3)Set 接口的遍历方式

同Collection的遍历方式一样,因为Set接口是Collection接口的子接口。

① 可以使用迭代器;

② 增强for;

③ 不能使用索引的方式来获取。

(4)Set接口的常用方法举例

@SuppressWarnings({"all"})
public class SetMethod {
    public static void main(String[] args) {
        //解读
        //1. 以Set 接口的实现类 HashSet 来讲解Set 接口的方法
        //2. set 接口的实现类的对象(Set接口对象), 不能存放重复的元素, 可以添加一个null
        //3. set 接口对象存放数据是无序(即添加的顺序和取出的顺序不一致)
        //4. 注意:取出的顺序的顺序虽然不是添加的顺序,但是他的固定.
        Set set = new HashSet();
        set.add("john");
        set.add("lucy");
        set.add("john");//重复
        set.add("jack");
        set.add("hsp");
        set.add("mary");
        set.add(null);//
        set.add(null);//再次添加null
        for(int i = 0; i <10;i ++) {
            System.out.println("set=" + set);
        }
 
        //遍历
        //方式1: 使用迭代器
        System.out.println("=====使用迭代器====");
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object obj =  iterator.next();
            System.out.println("obj=" + obj);
 
        }
 
        set.remove(null);
 
        //方式2: 增强for
        System.out.println("=====增强for====");
 
        for (Object o : set) {
            System.out.println("o=" + o);
        }
 
        //set 接口对象,不能通过索引来获取
 
 
    }
}

2、Set 接口实现类-HashSet

(1)HashSet的全面说明

① HashSet实现了Set接口;

② HashSet实际上是HashMap;
③ 可以存放null值,但是只能有一个null;

④ HashSet不保证元素是有序的,取决于hash后,再确定索引的结果;(即,不保证存放元素的顺序和取出顺序一致)

⑤ 不能有重复元素/对象。

(2)HashSet案例说明

@SuppressWarnings({"all"})
public class HashSet01 {
    public static void main(String[] args) {
        HashSet set = new HashSet();
 
        //说明
        //1. 在执行add方法后,会返回一个boolean值
        //2. 如果添加成功,返回 true, 否则返回false
        //3. 可以通过 remove 指定删除哪个对象
        System.out.println(set.add("john"));//T
        System.out.println(set.add("lucy"));//T
        System.out.println(set.add("john"));//F
        System.out.println(set.add("jack"));//T
        System.out.println(set.add("Rose"));//T
 
 
        set.remove("john");
        System.out.println("set=" + set);//3个
 
        //
        set  = new HashSet();
        System.out.println("set=" + set);//0
        //4 Hashset 不能添加相同的元素/数据?
        set.add("lucy");//添加成功
        set.add("lucy");//加入不了
        set.add(new Dog("tom"));//OK
        set.add(new Dog("tom"));//Ok
        System.out.println("set=" + set);
 
        //在加深一下. 非常经典的面试题.
        //看源码,做分析, 先给小伙伴留一个坑,以后讲完源码,你就了然
        //去看他的源码,即 add 到底发生了什么?=> 底层机制.
        set.add(new String("hsp"));//ok
        set.add(new String("hsp"));//加入不了.
        System.out.println("set=" + set);
 
 
    }
}
class Dog { //定义了Dog类
    private String name;
 
    public Dog(String name) {
        this.name = name;
    }
 
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                '}';
    }
}

在这里插入图片描述
在这里插入图片描述

@SuppressWarnings({"all"})
public class HashSetStructure {
    public static void main(String[] args) {
        //模拟一个HashSet的底层 (HashMap 的底层结构)
 
        //1. 创建一个数组,数组的类型是 Node[]
        //2. 有些人,直接把 Node[] 数组称为 表
        Node[] table = new Node[16];
 
        //3. 创建结点
        Node john = new Node("john", null);
 
        table[2] = john;
        Node jack = new Node("jack", null);
        john.next = jack;// 将jack 结点挂载到john
        Node rose = new Node("Rose", null);
        jack.next = rose;// 将rose 结点挂载到jack
 
        Node lucy = new Node("lucy", null);
        table[3] = lucy; // 把lucy 放到 table表的索引为3的位置.
        System.out.println("table=" + table);
 
 
    }
}
class Node { //结点, 存储数据, 可以指向下一个结点,从而形成链表
    Object item; //存放数据
    Node next; // 指向下一个结点
 
    public Node(Object item, Node next) {
        this.item = item;
        this.next = next;
    }
}

(3)HashSet底层

在这里插入图片描述

@SuppressWarnings({"all"})
public class HashSetSource {
    public static void main(String[] args) {
 
        HashSet hashSet = new HashSet();
        hashSet.add("java");//到此位置,第1次add分析完毕.
        hashSet.add("php");//到此位置,第2次add分析完毕
        hashSet.add("java");
        System.out.println("set=" + hashSet);
 
        /*
        对HashSet 的源码解读
        1. 执行 HashSet()
            public HashSet() {
                map = new HashMap<>();
            }
        2. 执行 add()
           public boolean add(E e) {//e = "java"
                return map.put(e, PRESENT)==null;//(static) PRESENT = new Object();
           }
         3.执行 put() , 该方法会执行 hash(key) 得到key对应的hash值 算法h = key.hashCode()) ^ (h >>> 16)
             public V put(K key, V value) {//key = "java" value = PRESENT 共享
                return putVal(hash(key), key, value, false, true);
            }
         4.执行 putVal
         final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
                Node<K,V>[] tab; Node<K,V> p; int n, i; //定义了辅助变量
                //table 就是 HashMap 的一个数组,类型是 Node[]
                //if 语句表示如果当前table 是null, 或者 大小=0
                //就是第一次扩容,到16个空间.
                if ((tab = table) == null || (n = tab.length) == 0)
                    n = (tab = resize()).length;
                //(1)根据key,得到hash 去计算该key应该存放到table表的哪个索引位置
                //并把这个位置的对象,赋给 p
                //(2)判断p 是否为null
                //(2.1) 如果p 为null, 表示还没有存放元素, 就创建一个Node (key="java",value=PRESENT)
                //(2.2) 就放在该位置 tab[i] = newNode(hash, key, value, null)
                if ((p = tab[i = (n - 1) & hash]) == null)
                    tab[i] = newNode(hash, key, value, null);
                else {
                    //一个开发技巧提示: 在需要局部变量(辅助变量)时候,在创建
                    Node<K,V> e; K k; //
                    //如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样
                    //并且满足 下面两个条件之一:
                    //(1) 准备加入的key 和 p 指向的Node 结点的 key 是同一个对象
                    //(2)  p 指向的Node 结点的 key 的equals() 和准备加入的key比较后相同
                    //就不能加入
                    if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
                        e = p;
                    //再判断 p 是不是一颗红黑树,
                    //如果是一颗红黑树,就调用 putTreeVal , 来进行添加
                    else if (p instanceof TreeNode)
                        e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                    else {//如果table对应索引位置,已经是一个链表, 就使用for循环比较
                          //(1) 依次和该链表的每一个元素比较后,都不相同, 则加入到该链表的最后
                          //    注意在把元素添加到链表后,立即判断 该链表是否已经达到8个结点
                          //    , 就调用 treeifyBin() 对当前这个链表进行树化(转成红黑树)
                          //    注意,在转成红黑树时,要进行判断, 判断条件
                          //    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
                          //            resize();
                          //    如果上面条件成立,先table扩容.
                          //    只有上面条件不成立时,才进行转成红黑树
                          //(2) 依次和该链表的每一个元素比较过程中,如果有相同情况,就直接break
                        for (int binCount = 0; ; ++binCount) {
                            if ((e = p.next) == null) {
                                p.next = newNode(hash, key, value, null);
                                if (binCount >= TREEIFY_THRESHOLD(8) - 1) // -1 for 1st
                                    treeifyBin(tab, hash);
                                break;
                            }
                            if (e.hash == hash &&
                                ((k = e.key) == key || (key != null && key.equals(k))))
                                break;
                            p = e;
                        }
                    }
                    if (e != null) { // existing mapping for key
                        V oldValue = e.value;
                        if (!onlyIfAbsent || oldValue == null)
                            e.value = value;
                        afterNodeAccess(e);
                        return oldValue;
                    }
                }
                ++modCount;
                //size 就是我们每加入一个结点Node(k,v,h,next), size++
                if (++size > threshold)
                    resize();//扩容
                afterNodeInsertion(evict);
                return null;
            }
         */
 
    }
}

(4)HashSet 课堂练习

在这里插入图片描述

@SuppressWarnings({"all"})
public class HashSetExercise {
    public static void main(String[] args) {
 
 
        /**
         定义一个Employee类,该类包含:private成员属性name,age 要求:
         创建3个Employee 对象放入 HashSet中
         当 name和age的值相同时,认为是相同员工, 不能添加到HashSet集合中
         */
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("milan", 18));//ok
        hashSet.add(new Employee("smith", 28));//ok
        hashSet.add(new Employee("milan", 18));//加入不成功.
 
        //回答,加入了几个? 3个
        System.out.println("hashSet=" + hashSet);
    }
}
 
//创建Employee
class Employee {
    private String name;
    private int age;
 
    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
 
    public void setAge(int age) {
        this.age = age;
    }
    //如果name 和 age 值相同,则返回相同的hash值
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age &&
                Objects.equals(name, employee.name);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
 

在这里插入图片描述

@SuppressWarnings({"all"})
public class HashSetPractice01 {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee01("小李",30000,new MyDate(1999,6,14)));
        hashSet.add(new Employee01("小李",30000,new MyDate(1999,6,14)));
        for (Object o :hashSet) {
            System.out.println(o);
        }
 
    }
}
@SuppressWarnings({"all"})
class Employee01{
    private String name;
    private double sal;
    private MyDate birthday;
 
    public Employee01(String name, double sal, MyDate birthday) {
        this.name = name;
        this.sal = sal;
        this.birthday = birthday;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public double getSal() {
        return sal;
    }
 
    public void setSal(double sal) {
        this.sal = sal;
    }
 
    public MyDate getBirthday() {
        return birthday;
    }
 
    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }
 
    @Override
    public String toString() {
        return "Employee01{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", birthday=" + birthday +
                '}';
    }
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee01 that = (Employee01) o;
        return Objects.equals(name, that.name) &&
                Objects.equals(birthday, that.birthday);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(name, birthday);
    }
}
@SuppressWarnings({"all"})
class MyDate{
    private int year;
    private int month;
    private int day;
 
    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
 
    public int getYear() {
        return year;
    }
 
    public void setYear(int year) {
        this.year = year;
    }
 
    public int getMonth() {
        return month;
    }
 
    public void setMonth(int month) {
        this.month = month;
    }
 
    public int getDay() {
        return day;
    }
 
    public void setDay(int day) {
        this.day = day;
    }
 
    @Override
    public String toString() {
        return "MyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyDate myDate = (MyDate) o;
        return year == myDate.year &&
                month == myDate.month &&
                day == myDate.day;
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(year, month, day);
    }
 
}

3、Set 接口实现类-LinkedHashSet

(1)LinkedHashSet 的全面说明

① LinkedHashSet 是 HashSet 的子类;

② LinkedHashSet 底层是一个 LinkedHashMap,底层维护了一个数组+双向链表;

③ LinkedHashSet 根据元素 hashCode来决定元素的存储位置,同时使用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的;

④ LinkedHashSet 不允许添加重复元素。
在这里插入图片描述

@SuppressWarnings({"all"})
public class LinkedHashSetSource {
    public static void main(String[] args) {
        //分析一下LinkedHashSet的底层机制
        Set set = new LinkedHashSet();
        set.add(new String("AA"));
        set.add(456);
        set.add(456);
        set.add(new Customer("刘", 1001));
        set.add(123);
        set.add("HSP");
 
        System.out.println("set=" + set);
        //解读
        //1. LinkedHashSet 加入顺序和取出元素/数据的顺序一致
        //2. LinkedHashSet 底层维护的是一个LinkedHashMap(是HashMap的子类)
        //3. LinkedHashSet 底层结构 (数组table+双向链表)
        //4. 添加第一次时,直接将 数组table 扩容到 16 ,存放的结点类型是 LinkedHashMap$Entry
        //5. 数组是 HashMap$Node[] 存放的元素/数据是 LinkedHashMap$Entry类型
        /*
                //继承关系是在内部类完成.
                static class Entry<K,V> extends HashMap.Node<K,V> {
                    Entry<K,V> before, after;
                    Entry(int hash, K key, V value, Node<K,V> next) {
                        super(hash, key, value, next);
                    }
                }
         */
 
    }
}
class Customer {
    private String name;
    private int no;
 
    public Customer(String name, int no) {
        this.name = name;
        this.no = no;
    }
}

(2)LinkedHashSet练习题

在这里插入图片描述

@SuppressWarnings({"all"})
public class LinkedHashSetExercise {
    public static void main(String[] args) {
 
        LinkedHashSet linkedHashSet = new LinkedHashSet();
        linkedHashSet.add(new Car("奥拓", 1000));//OK
        linkedHashSet.add(new Car("奥迪", 300000));//OK
        linkedHashSet.add(new Car("法拉利", 10000000));//OK
        linkedHashSet.add(new Car("奥迪", 300000));//加入不了
        linkedHashSet.add(new Car("保时捷", 70000000));//OK
        linkedHashSet.add(new Car("奥迪", 300000));//加入不了
 
        System.out.println("linkedHashSet=" + linkedHashSet);
 
    }
}
 
/**
 * Car 类(属性:name,price),  如果 name 和 price 一样,
 * 则认为是相同元素,就不能添加。 5min
 */
 
class Car {
    private String name;
    private double price;
 
    public Car(String name, double price) {
        this.name = name;
        this.price = price;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public double getPrice() {
        return price;
    }
 
    public void setPrice(double price) {
        this.price = price;
    }
 
    @Override
    public String toString() {
        return "\nCar{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
 
    //重写equals 方法 和 hashCode
    //当 name 和 price 相同时, 就返回相同的 hashCode 值, equals返回t
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return Double.compare(car.price, price) == 0 &&
                Objects.equals(name, car.name);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动驾驶开源数据集(附下载链接)

自动驾驶是带动新兴产业的一个突破点&#xff0c;也是中国结合新能源汽车&#xff0c;实现汽车产业弯道超车的不二手段&#xff0c;是打破国外燃油车技术壁垒的关键一步&#xff01;它不会停止&#xff0c;只是在蓄势待发&#xff01; 数据集介绍&#xff1a;点击 自动驾驶场…

java进阶—通俗易懂线程池的概念(底层原理)及使用

前言 首先&#xff0c;我们知道创建一个线程 可以直接 使用 new Thread(() ->{}).start();这种形式来创建&#xff0c;当线程的run 方法执行结束&#xff0c;线程就终止了&#xff0c;线程对象就会被垃圾回收机制&#xff08;GC&#xff09;释放 然而在我们 开发工作中&…

智安网络|攻防演练对抗:网络边界自动化防御的关键

在当今高度互联的数字世界中&#xff0c;网络安全的重要性日益凸显。为了应对不断增长的网络威胁&#xff0c;组织和企业需要采取主动的防御策略&#xff0c;其中攻防演练对抗和自动化防御在保护网络边界方面扮演着重要的角色。本文将探讨攻防演练对抗的意义&#xff0c;并介绍…

四、用户管理

云尚办公系统&#xff1a;用户管理 B站直达【为尚硅谷点赞】: https://www.bilibili.com/video/BV1Ya411S7aT 本博文以课程相关为主发布&#xff0c;并且融入了自己的一些看法以及对学习过程中遇见的问题给出相关的解决方法。一起学习一起进步&#xff01;&#xff01;&#x…

ImportError: numpy.core.multiarray failed to import

遇到的问题&#xff1a; 解决方法&#xff1a; 根据你的opencv版本&#xff0c;去百度搜索对应的 numpy 版本&#xff0c;卸载掉现有的numpy &#xff0c;安装其他版本: sudo pip install numpy1.19.0或者直接升级到numpy的最新版本&#xff1a; sudo pip install --upgrade…

小程序-真机上接口无法调通,开发者工具上可以

近期在对接小程序&#xff0c;在这里记录一下&#xff0c;我们在对接小程序的时候碰到的一些奇奇怪怪的问题。 其中一个问题如下&#xff1a; 真实效果如下图 开发者工具上可以&#xff0c;访问没有人任何问题。 真机上接口无法调通&#xff0c;也没有报错&#xff0c;也没有…

idea中git的使用详细说明

一.克隆项目 1. 打开File>New>Project from Version Control... 2. 打开gitLab&#xff0c;复制项目地址 3. 粘贴到第1步中的URL中&#xff0c;点击“Clone” 二.代码提交 1. 修改文件后需要提交时&#xff0c;可以在git-Local Changes中看到你修改的文件及修改内容 2. 选…

netty学习(1):多个客户端与服务器通信

1. 基于前面一节netty学习&#xff08;1&#xff09;:1个客户端与服务器通信 只需要把服务器的handler改造一下即可&#xff0c;通过ChannelGroup 找到所有的客户端channel&#xff0c;发送消息即可。 package server;import io.netty.channel.*; import io.netty.channel.gr…

MS1826 HDMI 多功能视频处理器 4*4矩阵切换器

基本介绍 MS1826 是一款多功能视频处理器&#xff0c;包含 4 路独立 HDMI 音视频输入通道、4 路独立 HDMI 音视频输出通道以及四路独立可配置为输入或者输出的 SPDIF、I2S 音频信号。支持 4 个独立的字 库定制型 OSD&#xff1b;可处理隔行和逐行视频或者图形输入信号&#xff…

Spring Boot 中的 @ComponentScan 注解是什么,原理,如何使用

Spring Boot 中的 ComponentScan 注解是什么&#xff0c;原理&#xff0c;如何使用 在 Spring Boot 中&#xff0c;ComponentScan 是一种注解&#xff0c;它可以让 Spring 自动扫描指定的包及其子包中的组件&#xff0c;并将这些组件自动装配到 Spring 容器中。本文将介绍 Com…

UML14种图

UML14种图 UML是Unified Modeling Language的缩写&#xff0c;译为统一建模语言。 UML是软件行业的建模规范&#xff0c;可以对软件项目建立需求模型、设计模型、实现模型、测试模型。 UML2.0包含的14种图&#xff1a; UML各种图例&#xff08;常用图形&#xff09; 1. 类图&…

状态机编程实例-状态表法

上篇文章&#xff0c;使用嵌套switch-case法的状态机编程&#xff0c;实现了一个炸弹拆除小游戏。 本篇&#xff0c;继续介绍状态机编程的第二种方法&#xff1a;状态表法&#xff0c;来实现炸弹拆除小游戏的状态机编程。 1 状态表法 状态表法&#xff0c;顾名思义&#xff0…

YOLOv8的目标对象的分类,分割,跟踪和姿态估计的多任务检测实践(Netron模型可视化)

YOLOv8是目前最新版本&#xff0c;在以前YOLO版本基础上建立并加入了一些新的功能&#xff0c;以进一步提高性能和灵活性&#xff0c;是目前最先进的模型。YOLOv8旨在快速&#xff0c;准确&#xff0c;易于使用&#xff0c;使其成为广泛的目标检测和跟踪&#xff0c;实例分割&a…

MATLAB 之 Simulink 操作基础和系统仿真模型的建立

这里写目录标题 一、Simulink 操作基础1. Simulink 的启动与退出1.1 Simulink 的启动1.2 模型文件的打开1.3 Simulink 的退出 2. Simulink 仿真初步2.1 模型元素2.2 仿真步骤2.3 简单实例 二、系统仿真模型的建立1. Simulink 的基本模块2. 模块操作2.1 添加与删除模块2.2 选取模…

快速训练自己的大语言模型:基于LLAMA-7B的lora指令微调

目录 1. 选用工程&#xff1a;lit-llama2. 下载工程3. 安装环境4. 下载LLAMA-7B模型5. 做模型转换6. 初步测试7. 为什么要进行指令微调&#xff1f;8. 开始进行指令微调8.1. 数据准备8.2 开始模型训练8.3 模型测试 前言&#xff1a; 系统&#xff1a;ubuntu 18.04显卡&#xff…

大数据ETL工具对比(Sqoop, DataX, Kettle)

前言 在实习过程中&#xff0c;遇到了数据库迁移项目&#xff0c;对于数据仓库&#xff0c;大数据集成类应用&#xff0c;通常会采用ETL工具辅助完成&#xff0c;公司和客户使用的比较多的是Sqoop, DataX和Kettle这三种工具。简单的对这三种ETL工具进行一次梳理。 ETL工具&…

无法更新iPhone,提示“无法检查更新”怎么办?

当我们需要 iPhone更新系统时&#xff0c;可以前往iPhone设置-通用-软件更新中获取更新推送。不过一些用户可能会遇到无法更新的问题&#xff0c;例如会提示“无法检查更新&#xff0c;检查软件更新时出错”。 以上情况可能是网络问题&#xff0c;可以尝试重新打开设置&#xf…

vue2实现公式规则编辑校验弹窗功能

文章目录 需求描述技术栈最终效果演示功能实现逻辑拆分代码目录结构实现思路光标实现底部单个符号或字段结构设计监听键盘事件&处理光标公式规则校验 总结 需求描述 需要一个弹窗&#xff0c;弹窗内部需要能够进行公式规则的配置并进行公式规则合法性校验。 技术栈 vue2e…

Thymeleaf的常用语法

&#x1f31f; Thymeleaf的常用语法 Thymeleaf是一个Java模板引擎&#xff0c;用于处理HTML、XML、JavaScript、CSS等文件。它可以与Spring框架无缝集成&#xff0c;为Web应用程序提供优雅的模板解决方案。本文将介绍Thymeleaf的常用语法&#xff0c;包括th属性、表达式、内置…

Spring Boot 中的 @RestController 注解,如何使用

Spring Boot 中的 RestController 注解 在 Spring Boot 中&#xff0c;我们经常需要编写 RESTful Web 服务&#xff0c;以便于客户端与服务器之间的通信。为了简化 RESTful Web 服务的开发&#xff0c;Spring Boot 提供了 RestController 注解&#xff0c;它可以让我们更方便地…