Spring Boot 3.3 【九】Redis 的五种数据结构深入浅出(String List Set Hash Zset)

news2024/11/5 19:30:24

如果觉得本文能够帮到您,请关注🌟、点赞👍、收藏📚,让这份美好延续下去!

一、Redis 数据结构简介

在现代应用开发中,高效的数据存储和管理是构建强大系统的关键。Redis 作为一种高性能的内存数据库,以其丰富的数据结构和快速的操作能力而备受青睐。Spring Boot 3 作为流行的开发框架,为整合 Redis 提供了便捷的方式。

在本文中,我们将深入探讨 Spring Boot 3 如何与 Redis 进行整合,并详细介绍对 Redis 的五种主要数据结构 —— 字符串(String)、列表(List)、集合(Set)、哈希(Hash)和有序集合(Sorted Set)的操作。通过掌握这些操作,我们能够充分发挥 Redis 的优势,提升应用的性能和灵活性。

二、深入 Redis 的五种数据结构

(一)字符串

1. Redis String 简介

Redis 的字符串(String)是最基本的数据类型,由一个键和一个值组成,其中键和值都可以是字符串。其值的最大限制为 512MB,这使得它能够存储相对较大的数据量。String 类型是 Redis 中最为常用的数据类型之一,因为它支持多种操作,非常灵活。

它支持简单的 GET 和 SET 操作,可以快速地获取和设置字符串的值。此外,还支持自增(INCR)和自减(DECR)操作,这使得它非常适合用于实现 计数器功能,比如 统计网站的访问量点赞数等。同时,还可以进行字符串拼接等操作,进一步扩展了其应用场景。

典型的应用场景 包括缓存数据,比如 存储用户登录状态Token 以及各种 配置信息等。在这些场景中,String 类型可以快速地存储和读取数据,提高系统的响应速度。另外,结合 SETNX 命令,还可以用字符串来实现简单的 分布式锁,保证在分布式环境下对共享资源的互斥访问。

底层原理方面,Redis 底层对字符串使用的是简单动态字符串(SDS)。SDS 不仅仅是对 C 字符串的简单封装,它还加入了长度属性,使得获取字符串长度的操作时间复杂度为 O (1)。同时,SDS 还采用了空间预留策略,当进行字符串拼接等操作时,可以减少内存分配的次数,提高性能。此外,SDS 支持二进制安全,可以存储文本和二进制数据,这使得它在存储各种类型的数据时更加灵活。

2. redisTemplate.opsForValue().set() 方法

public void setString(String key, String value) {
    redisTemplate.opsForValue().set(key, value);
}

这个方法用于将一个字符串值存储到 Redis 中。通过 redisTemplate.opsForValue() 获取到针对字符串类型的操作对象,然后调用 set 方法,传入键和值,将指定的值存储到 Redis 中,与该键对应。如果键已经存在,会覆盖原有值。

3. redisTemplate.opsForValue().get() 方法

public String getString(String key) {
    return (String) redisTemplate.opsForValue().get(key);
}

此方法用于从 Redis 中获取与给定键对应的值。同样通过 redisTemplate.opsForValue() 获取操作对象,然后调用 get 方法传入键,返回存储在 Redis 中的字符串值。如果键不存在,将返回 null。

在这里插入图片描述

(二)列表

1. Redis List 简介

Redis 的列表(List)是一个 双向链表,可以从头部或尾部插入、删除元素。常用的命令包括 LPUSH(从头部插入元素)、RPUSH(从尾部插入元素)、LPOP(从头部弹出元素)、RPOP(从尾部弹出元素)等。

Redis 的列表还支持阻塞操作,如 BLPOP 和 BRPOP。当列表为空时,这些命令可以阻塞等待,直到有元素被插入到列表中。这种阻塞操作使得列表非常适合用于实现 消息队列 等场景。

典型的应用场景之一是作为消息队列。可以使用 LPUSH 将消息放入队列的头部,使用 RPOP 或 BRPOP 从队列的尾部弹出消息进行处理。这种方式实现的消息队列简单高效,适用于各种异步处理场景。另一个应用场景是 任务调度,在异步任务分发系统中,可以将任务放入列表中,由多个消费者去消费任务,实现任务的并行处理。

列表采用 双向链表(quicklist) 实现。对于较短的列表,Redis 会使用 压缩列表(ziplist) 来节省内存。压缩列表是一种紧凑的 内存数据结构,可以在一定程度上减少内存占用。但是,当列表长度增加时,为了保证操作的时间复杂度,Redis 会自动将其转换为真正的双向链表。这样可以在不同的场景下平衡内存使用和操作效率。

2. redisTemplate.opsForList().leftPush 方法

(1)以下方法将一个元素插入到指定列表的左侧。
使用 redisTemplate.opsForList() 获取针对列表类型的操作对象,调用 leftPush 方法,传入键和要插入的元素值。随着元素不断插入,列表的头部不断变化,最先插入的元素会逐渐移向列表的尾部。

public void leftPush(String key, Object value) {
    redisTemplate.opsForList().leftPush(key, value);
}

(2)依次向 list 中存入元素 aaa,bbb,ccc,aaa,ddd

  @PostMapping("/leftPush")
    public void leftPush() {	
    	String aaa = "aaa", bbb="bbb", ccc="ccc", ddd="ddd";
        userService.leftPush(aaa);
        userService.leftPush(bbb);
        userService.leftPush(ccc);
        userService.leftPush(aaa);
        userService.leftPush(ddd);
    }

(3)可以看到 redis 中,先存储的在右侧(底部),后存储的在左侧(上部):

在这里插入图片描述

3. redisTemplate.opsForList().rightPush 方法

(1)该方法是从列表 右侧 插入元素:

  public void rightPush(String key, Object value) {
        redisTemplate.opsForList().rightPush(key, value);
    }
  @PostMapping("/rightPush")
    public void rightPush() {	
    	String eee = "eee", fff="fff";
        userService.rightPush(eee);
        userService.rightPush(fff);
    
    }

(2)查看 redis中 的元素,eeefff 是从列表右侧插入:

在这里插入图片描述

4. 从列表左侧和右侧取出元素

(1)可以从列表左侧或者右侧取出(删除)一个元素。
即使用 leftPoprightPop 方法,从列表的左侧或右侧移除并返回一个元素。如果列表为空,将返回 null。

 /*
     * 从左侧取出一个元素
     */
    public Object leftPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }
    
    /*
     * 从右侧取出一个元素
     */
    public Object rightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

(2)从左侧取出了元素 ddd, 从右侧取出了元素 fff

在这里插入图片描述

5. 获取列表长度

(1)redisTemplate.opsForList().size() 方法:

	// 获取列表长度
    public Long listLength(String key) {
        return redisTemplate.opsForList().size(key);
    }

(2)请求结果为 5
在这里插入图片描述

6. 获取列表指定范围的元素

(1)以下方法的作用是获取指定键对应列表中从 start 索引到 end 索引范围内的元素。

它通过调用 redisTemplate.opsForList().range(key, start, end) 实现,可用于获取列表的一部分进行处理或展示,其中列表的第一个元素从 0 开始。

	// 获取列表指定范围的元素
    public List<Object> range(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

(2)Redis 列表的原数据如下图所示:

在这里插入图片描述

(3)比如获取列表从索引 1(第二个元素) 开始 至 索引 4

   /*
     * 获取列表指定范围元素
     */
    @GetMapping("range")
    public List<Object> range(String key) {
        return userService.range(key, 1, 4);
    }

得出如下结果:

在这里插入图片描述
(4)获取整个列表
输入起始位置为 0,

	/*
     * 获取列表指定元素
     */
    @GetMapping("range")
    public List<Object> range(String key) {
        return userService.range(key, 0, -1);
    }

(三)无序集合

1. Redis Set 简介

Redis 的集合(Set)是一个 无序的唯一的 元素集合。这意味着集合中的元素是唯一的,不会有重复元素。集合提供了类似于数学集合的操作,支持交集(SINTER)、并集(SUNION)、差集(SDIFF)等。

常见的 Redis 操作 包括 SADD 用于向集合中添加元素、SREM 用于从集合中删除元素、SISMEMBER 用于判断一个元素是否在集合中、SMEMBERS 用于获取集合中的所有元素等。

典型的应用场景 之一是 标签系统。可以将用户标签存储为集合,每个集合代表一个用户群体。通过集合的交集、并集和差集等操作,可以方便地找出同时拥有某几个标签的用户,或者找出具有特定标签组合的用户群体。另一个应用场景是 去重功能。在某些场景下,比如 热门搜索词访问日志的去重等,可以通过集合的唯一性特性来避免重复数据的存储和处理。

集合在 小集合 时使用 整数集合(intset) 来存储元素。整数集合是一种紧凑的整数存储结构,可以节省内存。当集合中的元素数量增加或者元素类型不是整数时,会自动转换为 哈希表(hashtable) 实现。通过哈希表的快速查找特性,可以实现 O (1) 的时间复杂度来判断元素是否存在于集合中。

2. 代码示例

Redis Util 工具类的各个方法对应 Redis 集合的不同操作:

package com.jsglxx.redis;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class CacheUtil {

    private final RedisTemplate<String, Object> redisTemplate;

    public CacheUtil(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // 向集合中添加元素
    public void addToSet(String key, Object value) {
        redisTemplate.opsForSet().add(key, value);
    }

    // 从集合中移除元素
    public void removeFromSet(String key, Object value) {
        redisTemplate.opsForSet().remove(key, value);
    }

    // 判断元素是否在集合中
    public boolean isMemberOfSet(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    // 获取集合中的所有元素
    public Set<Object> getSetMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }
    
}
  • addToSet 方法使用 redisTemplate.opsForSet().add(key, value) 向指定键对应的集合中添加元素。
  • removeFromSet 方法使用 redisTemplate.opsForSet().remove(key, value) 从集合中移除指定元素。
  • isMemberOfSet 方法使用 redisTemplate.opsForSet().isMember(key, value) 判断给定元素是否在集合中。
  • getSetMembers 方法使用 redisTemplate.opsForSet().members(key) 获取集合中的所有元素。

3. 测试方法:

在以下测试方法中,首先向两个集合中添加元素,然后分别测试各个方法,输出结果以验证方法的正确性。

public void testSet() {
		String setKey1 = "set1";
        String setKey2 = "set2";

        // 向集合 1 添加元素
        cacheUtil.addToSet(setKey1, "element1");
        cacheUtil.addToSet(setKey1, "element2");
        cacheUtil.addToSet(setKey1, "element3");

        // 向集合 2 添加元素
        cacheUtil.addToSet(setKey2, "element2");
        cacheUtil.addToSet(setKey2, "element3");
        cacheUtil.addToSet(setKey2, "element4");

        // 判断元素是否在集合中
        boolean isMember = cacheUtil.isMemberOfSet(setKey1, "element2");
        System.out.println("【元素element2】在【集合set1】中吗? " + isMember);

        // 获取集合中的所有元素
        Set<Object> setMembers1 = cacheUtil.getSetMembers(setKey1);
        System.out.println("【集合set1】中的所有元素: " + setMembers1);

        Set<Object> setMembers2 = cacheUtil.getSetMembers(setKey2);
        System.out.println("【集合set2】中的所有元素: " + setMembers2);
	}

4. 结果输出

【元素element2】在【集合set1】中吗? true
【集合set1】中的所有元素: [element1, element2, element3]
【集合set2】中的所有元素: [element2, element3, element4]

(四)Hash

1. Redis 哈希介绍

Redis 的哈希(Hash)是一个 =键值对集合,非常适合用于存储对象。每个键可以有多个字段,每个字段都对应一个值。这种结构使得可以将一个复杂的对象拆分成多个字段进行存储,方便进行管理和查询。

常用的 Redis 操作包括 HSET 用于设置字段的值、HGET 用于获取字段的值、HDEL 用于删除特定字段等。这些操作使得对哈希的管理非常方便,可以根据需要快速地添加、修改和删除字段。

典型的应用场景 之一是 存储用户信息。可以将用户 ID 作为键,用户的各种属性(如姓名、年龄、性别等)作为字段,存储在哈希中。这样可以避免将整个用户对象序列化成字符串进行存储,在查询和更新用户信息时更加高效。另一个应用场景是 配置项管理,将不同的配置项存储在哈希中,方便根据字段名快速访问和更新某个特定的配置。

哈希使用了两种底层数据结构。在 小数据量 时,使用 压缩列表(ziplist) 来存储哈希。压缩列表是一种紧凑的内存数据结构,可以节省内存空间。但是,随着哈希表的增长,当数据量达到一定程度时,会自动转换为 哈希表(hashtable)。哈希表具有更高的查询效率,可以保证在大数据量时仍然能够快速地进行字段的查找和操作。

2. Redis 工具类代码示例

package com.jsglxx.redis;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class CacheUtil {

    private final RedisTemplate<String, Object> redisTemplate;

    public CacheUtil(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    // 设置哈希字段的值
    public void setHashField(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    // 获取哈希字段的值
    public Object getHashField(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    // 删除哈希字段
    public void deleteHashField(String key, String... fields) {
        redisTemplate.opsForHash().delete(key, fields);
    }

    // 获取哈希的所有字段名
    public Set<Object> getHashKeys(String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    // 获取哈希的所有值
    public List<Object> getHashValues(String key) {
        return redisTemplate.opsForHash().values(key);
    }

    // 获取哈希的所有字段和值
    public Map<Object, Object> getHashAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
}
  • setHashField 方法使用 redisTemplate.opsForHash().put(key, field, value) 将指定的值设置到哈希表中给定的字段。
  • getHashField 方法使用 redisTemplate.opsForHash().get(key, field) 获取哈希表中指定字段的值。
  • deleteHashField 方法使用 redisTemplate.opsForHash().delete(key, fields) 删除哈希表中的一个或多个字段。
  • getHashKeys 方法使用 redisTemplate.opsForHash().keys(key) 获取哈希表中的所有字段名。
  • getHashValues 方法使用 redisTemplate.opsForHash().values(key) 获取哈希表中的所有值。
  • getHashAll 方法使用 redisTemplate.opsForHash().entries(key) 获取哈希表中的所有字段和值,以一个 Map 的形式返回。

3. 测试类代码

在测试类中,首先设置哈希表的字段值,然后分别测试各个方法,输出结果以验证方法的正确性。

public void testHash() {
		
	String hashKey = "myHash";

	// 设置哈希字段的值
	cacheUtil.setHashField(hashKey, "field1", "value1");
	cacheUtil.setHashField(hashKey, "field2", "value2");
	cacheUtil.setHashField(hashKey, "field3", "value3");

	// 获取哈希字段的值
	Object value = cacheUtil.getHashField(hashKey, "field2");
	System.out.println("field2 的值为: " + value);

	// 删除哈希字段
	cacheUtil.deleteHashField(hashKey, "field1");

	// 获取哈希的所有字段名
	Set<Object> keys = cacheUtil.getHashKeys(hashKey);
	System.out.println("Hash表中的所有键: " + keys);

	// 获取哈希的所有值
	List<Object> values = cacheUtil.getHashValues(hashKey);
	System.out.println("Hash表中的所有值: " + values);

	// 获取哈希的所有字段和值
	Map<Object, Object> allEntries = cacheUtil.getHashAll(hashKey);
	System.out.println("hash表中的所有键值对: " + allEntries);
}

4. 输出结果

field2 的值为: value2
Hash表中的所有键: [field2, field3]
Hash表中的所有值: [value2, value3]
hash表中的所有键值对: {field2=value2, field3=value3}

(五)有序集合

1. Redis Zset 简介

Redis 的有序集合是一种特殊的集合,其中 每个元素都关联一个分数。集合中的元素会按照 分数排序。支持的操作包括 ZADD 用于向有序集合中添加元素并指定分数、ZRANGE 和 ZREVRANGE 分别用于按照分数从小到大和从大到小获取元素、ZCOUNT 用于统计分数范围内的元素数量等。

典型的应用场景之一是 排行榜。比如在游戏中,可以将玩家的分数作为有序集合的元素分数,玩家 ID 作为元素,通过 ZADD 添加玩家及其分数。然后可以使用 ZRANGE 或 ZREVRANGE 获取排名靠前的玩家。另一个应用场景是 延迟任务。可以通过设置元素的分数为任务执行的时间,将任务存储在有序集合中。然后按照时间从集合中取出需要执行的任务进行处理。

有序集合底层使用的是 跳表(Skiplist)哈希表 相结合的数据结构。跳表是一种高效的有序数据结构,它使有序集合支持快速的范围查询和插入操作,时间复杂度为 O (log n)。而哈希表则保证了元素的快速定位,使得可以在 O (1) 的时间复杂度内判断元素是否存在于有序集合中。这种结合的方式既保证了查询效率,又能够有效地管理元素的顺序。

2. Redis 工具类代码示例

package com.jsglxx.redis;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class CacheUtil {

    private final RedisTemplate<String, Object> redisTemplate;

    public CacheUtil(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }  

    // 添加成员到有序集合
    public void addToSortedSet(String key, double score, Object member) {
        redisTemplate.opsForZSet().add(key, member, score);
    }

    // 从有序集合中移除成员
    public void removeFromSortedSet(String key, Object... members) {
        redisTemplate.opsForZSet().remove(key, members);
    }

    // 获取成员的分值
    public Double getScore(String key, Object member) {
        return redisTemplate.opsForZSet().score(key, member);
    }

    // 获取有序集合指定范围的成员(从小到大排序)
    public Set<Object> getRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    // 获取有序集合指定范围的成员(从大到小排序)
    public Set<Object> getReverseRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    // 获取分值范围内的成员(从小到大排序)
    public Set<Object> getRangeByScore(String key, double minScore, double maxScore) {
        return redisTemplate.opsForZSet().rangeByScore(key, minScore, maxScore);
    }

    // 获取分值范围内的成员(从大到小排序)
    public Set<Object> getReverseRangeByScore(String key, double maxScore, double minScore) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, maxScore, minScore);
    }

    // 获取有序集合的成员数量
    public Long getCardinality(String key) {
        return redisTemplate.opsForZSet().zCard(key);
    }

    // 获取分值范围内的成员数量
    public Long getCountByScore(String key, double minScore, double maxScore) {
        return redisTemplate.opsForZSet().count(key, minScore, maxScore);
    }
    
}
  • addToSortedSet 方法使用 redisTemplate.opsForZSet().add(key, member, score) 向有序集合添加成员和分值。
  • removeFromSortedSet 方法使用 redisTemplate.opsForZSet().remove(key, members) 从有序集合中移除指定成员。
  • getScore 方法使用 redisTemplate.opsForZSet().score(key, member) 获取指定成员的分值。
  • getRangegetReverseRange 方法分别用于获取有序集合指定范围的成员,按照分值从小到大和从大到小排序。
  • getRangeByScoregetReverseRangeByScore 方法用于获取分值范围内的成员,同样。按照分值从小到大和从大到小排序。
  • getCardinality 方法使用 redisTemplate.opsForZSet().zCard(key) 获取有序集合的成员数量。
  • getCountByScore 方法使用 redisTemplate.opsForZSet().count(key, minScore, maxScore) 获取分值范围内的成员数量。

3. 测试类代码

在测试类中,首先向有序集合添加成员,然后分别测试各个方法,输出结果以验证方法的正确性。最后,从有序集合中移除一个成员。

public void testSortedSetOperations() {
      String sortedSetKey = "mySortedSet";

       // 添加成员到有序集合
       cacheUtil.addToSortedSet(sortedSetKey, 10.0, "member1");
       cacheUtil.addToSortedSet(sortedSetKey, 20.0, "member2");
       cacheUtil.addToSortedSet(sortedSetKey, 15.0, "member3");

       // 获取成员的分值
       Double score = cacheUtil.getScore(sortedSetKey, "member2");
       System.out.println("member2 的分值: " + score);

       // 获取有序集合指定范围的成员(从小到大排序)
       Set<Object> range = cacheUtil.getRange(sortedSetKey, 0, 1);
       System.out.println("有序集合ascending(0-1)的成员从小到大排序: " + range);

       // 获取有序集合指定范围的成员(从大到小排序)
       Set<Object> reverseRange = cacheUtil.getReverseRange(sortedSetKey, 0, 1);
       System.out.println("有序集合ascending(0-1)的成员从大到小排序: " + reverseRange);

       // 获取分值范围内的成员(从小到大排序)
       Set<Object> rangeByScore = cacheUtil.getRangeByScore(sortedSetKey, 12.0, 18.0);
       System.out.println("ascending集合内分值在12.0 - 18.0之间的成员: " + rangeByScore);

       // 获取分值范围内的成员(从大到小排序)
       Set<Object> reverseRangeByScore = cacheUtil.getReverseRangeByScore(sortedSetKey, 12.0, 18.0);
       System.out.println("分值12.0 - 18.0之间的成员从大到小排序: " + reverseRangeByScore);

       // 获取有序集合的成员数量
       Long cardinality = cacheUtil.getCardinality(sortedSetKey);
       System.out.println("集合内成员数量: " + cardinality);

       // 获取分值范围内的成员数量
       Long countByScore = cacheUtil.getCountByScore(sortedSetKey, 10.0, 20.0);
       System.out.println("10.0-20.0分值范围内的成员数量: " + countByScore);

       // 从有序集合中移除成员
       cacheUtil.removeFromSortedSet(sortedSetKey, "member1");
   }

4. 输出结果

member2 的分值: 20.0
有序集合ascending(0-1)的成员从小到大排序: [member1, member3]
有序集合ascending(0-1)的成员从大到小排序: [member2, member3]
ascending集合内分值在12.0 - 18.0之间的成员: [member3]
分值12.0 - 18.0之间的成员从大到小排序: [member3]
集合内成员数量: 3
10.0-20.0分值范围内的成员数量: 3

三、结尾

总之,Spring Boot 3 与 Redis 的整合为开发者提供了强大的数据存储和处理能力。通过对 Redis 的五种数据结构的熟练操作,我们可以根据不同的业务需求选择合适的数据结构,实现高效的数据管理和快速的访问。无论是缓存数据、构建消息队列、存储用户信息还是进行复杂的排序操作,Redis 都能在 Spring Boot 3 应用中发挥重要作用。不断探索和优化 Redis 的使用,将有助于我们构建更加健壮、高效的应用程序,满足不断变化的业务需求。

🌟 对技术管理感兴趣 请扫码关注下方 ⬇ 【 技术管理修行】

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

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

相关文章

命令行参数、环境变量、地址空间

命令行参数&#xff1a; int main(int argc, char *argv[ ])&#xff0c;main的参数可带可不带。argc参数通常代表后面的char *argv的元素个数有多少。 在linux中会把输入的字符串存到char *argv[ ]中&#xff0c;在数组的结尾为NULL。 命令行参数可以让同一个程序可以通过不同…

持续优化,构建更好地 auto git commit 体验

几个月前&#xff0c;受到一篇推文的启发 https://x.com/mtrainier2020/status/1802941902964277379 &#xff0c;我突然想到可以借助 git alias 添加一些小命令&#xff0c;加速我的 git workflow 流程&#xff0c;于是我花了两个小时的时间进行工程封装&#xff0c;并发布了 …

C#-类:声明类、声明类对象

一&#xff1a;类的声明 class 类名 {//特征——成员变量//行为——成员方法//保护特征——成员属性//构造函数和析构函数//索引器//运算符重载//静态成员 }类名&#xff1a;帕斯卡 同一个语句块中的不同类 不能重名 二&#xff1a;声明类对象 2.1 类的声明 ≠ 类对象的声…

【学习enable_if模板, 学习unqiue_str 删除操作】

enable_if 是 C 标准库中的一个模板结构体&#xff0c;它用于条件编译和 SFINAE&#xff08;Substitution Failure Is Not An Error&#xff09;。enable_if 的主要作用是通过条件编译来控制模板的实例化&#xff0c;从而实现条件编译和 SFINAE。 1. enable_if 的基本用法如下…

AIGC产品经理高频面试题❗️看完的都拿高薪了

&#x1f914;️如果你正在考虑找AI产品经理相关的工作, 建议好好准备这些面试问题, 如果想要更加详细的回答可以直接询问海螺AI哦! 谁用了不说一句: AI真的太适合准备面试啦(&#x1f640;尖叫抛开 之后会分享更多岗位的面试题目, 以及如何用AI准备面试的tips哦! 大模型&A…

【VS+QT】联合开发踩坑记录

最新更新日期&#xff1a;2024/11/05 0. 写在前面 因为目前在做自动化产线集成软件开发相关的工作&#xff0c;需要用到QT&#xff0c;所以选择了VS联合开发&#xff0c;方便调试。学习QT的过程中也踩了很多坑&#xff0c;在此记录一下&#xff0c;提供给各位参考。 1. 环境配…

Windows下安装Maven并配置环境

在Windows下安装Maven、修改阿里云仓库、修改本地仓库路径&#xff0c;并为IntelliJ IDEA配置Maven&#xff0c;可以按照以下步骤进行操作&#xff1a; 1. 安装Maven 下载Maven&#xff1a; 访问 Apache Maven官网&#xff0c;下载最新版本的Maven二进制包&#xff08;zip格式…

基于SpringBoot+微信小程序+协同过滤算法+二维码订单位置跟踪的农产品销售平台-新

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; “农产品商城”小程序…

【题】C#-数组:二维数组

1. 将1~10000赋值给一个二维数组(100行100列) int[,] array new int[100,100]; int index 1; for(int i 0;i < array.GetLength(0);i){for(int j 0;j < array.GetLength(1);j){array[i,j] index;index;} }2. 将二维数组的右上半部分置零 int[,] array new int[4,…

启明云端乐鑫一级代理商,ESP-NOW无线通信协议,设备稳定连接控制新选择

在数字化浪潮的推动下&#xff0c;我们正步入一个由智能设备构成的全新世界。这些设备&#xff0c;从智能手机到智能家居&#xff0c;从可穿戴设备到工业自动化系统&#xff0c;都在以前所未有的速度改变着我们的生活。 在物联网(IoT)的世界里&#xff0c;无线通信协议扮演着至…

基于SpringBoot的植物园管理小程序【附源码】

基于SpringBoot的植物园管理小程序 效果如下&#xff1a; 系统登录页面 管理员主页面 商品订单管理页面 植物园信息管理页面 小程序主页面 小程序登录页面 植物信息查询推荐页面 研究背景 随着互联网技术的快速发展和移动设备的普及&#xff0c;线上管理已经成为各行各业提高…

如何使用Web-Check和cpolar实现安全的远程网站监测与管理

文章目录 前言1.关于Web-Check2.功能特点3.安装Docker4.创建并启动Web-Check容器5.本地访问测试6.公网远程访问本地Web-Check7.内网穿透工具安装8.创建远程连接公网地址9.使用固定公网地址远程访问 前言 本期给大家分享一个网站检测工具Web-Check&#xff0c;能帮你全面了解网…

数据中台一键大解析!

自从互联玩企业掀起了数据中台风&#xff0c;数据中台这个点马上就火起来了&#xff0c;短短几年数据中台就得到了极高的热度&#xff0c;一大堆企业也在跟风做数据中台&#xff0c;都把数据中台作为企业数字化转型的救命稻草&#xff0c;可是如果我告诉你数据中台并不是万能钥…

华为和思科的配置

vrrp和mstp 思路 vrrp是用来虚拟网关&#xff0c;噢&#xff0c;是虚拟一条虚拟网关 优先级&#xff0c;priority越大越优先&#xff0c;优先级相同&#xff0c;哪个的路由器的vrrp先起来&#xff0c;谁就是主 mstp是快速生成树协议&#xff0c;防止环路用的 优先级越小越优…

【Linux】从零开始使用多路转接IO --- epoll

当你偶尔发现语言变得无力时&#xff0c; 不妨安静下来&#xff0c; 让沉默替你发声。 --- 里则林 --- 从零开始认识多路转接 1 epoll的作用和定位2 epoll 的接口3 epoll工作原理4 实现epollserverV1 1 epoll的作用和定位 之前提过的多路转接方案select和poll 都有致命缺点…

电子商城购物平台的设计与开发+ssm(lw+演示+源码+运行)

摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;电子商城购物平台小程序被用户普遍使用&#xff0c;为方便…

5G时代已来:我们该如何迎接超高速网络?

内容概要 随着5G技术的普及&#xff0c;我们的生活似乎变得更加“科幻”了。想象一下&#xff0c;未来的智能家居将不仅仅是能够听你说“开灯”&#xff1b;它们可能会主动询问你今天心情如何&#xff0c;甚至会推荐你一杯“维他命C芒果榨汁”&#xff0c;帮助你抵御夏天的炎热…

超详细的finalshell安装nginx

一、nginx环境安装 1.安装gcc 安装nginx需要先将官网下载的源码进行编译&#xff0c;编译依赖gcc环境&#xff0c;如果没有gcc环境&#xff0c;需要安装gcc。 yum install gcc-c 出现选择是否下载安装文件&#xff0c;输入 y 2.安装PCRE PCRE(Perl Compatible Regular Expre…

运维工具之docker入门

1.容器与docker 1.什么是容器&#xff1f; 容器是一种轻量级的&#xff0c;可移植的软件运行环境。它将软件程序本身及软件依赖库打包在一起。可以在不同平台和系统上运行。 2.什么是LXC LXC就是Linux container,。LXC是一种虚拟化技术&#xff0c;可以在操作系统层级上为应…

【华为HCIP实战课程二十九】中间到中间系统协议IS-IS邻居关系建立和LSP详解,网络工程师

一、广播环境邻居关系建立详解 1、广播环境邻居关系建立 广播邻居关系采用三次握手,携带的邻居列表为接口的MAC来标识 2、LSP同步:3种报文(CSNP和PSNP和具体的LSP) CSNP作用类似DBD,请求者发送PSNP(类似LSR)来请求具体的LSP 广播网络LSP交互过程: R1-R2(DIS)--R3…