yx的学习日记

1:算是正式开始把。。。。。

还是看了一下redis的视频,今天主要学习的是redis的客户端,在下面总结,然后具体学习了一下算法的时间复杂度分析。。

2:JAVA

Redis客户端的学习

Redis客户端到底是个啥呢?简单来说,它就像是一个"接口",只不过这个接口的具体实现是由各种编程语言提供的。我们主要关注的是Java里的两个常用工具:`Jedis`和`Spring-Redis`。通过它们,Java可以提供一些类和方法,用来创建连接、操作Redis数据库。大体流程是:先建立连接,然后通过Redis对象来操作数据。 接下来,我具体讲一下`Jedis`和`Spring-Redis`的区别,以及我是怎么学习它们的。


Jedis

首先看`Jedis`,它的使用其实很简单。比如,我们可以这样写代码来连接Redis:

csharp 复制代码
private Jedis jedis;
@BeforeEach
void setUp() {
    // 设置Redis的地址和端口号
    jedis = new Jedis("192.168.100.128", 6379);
    // 设置密码
    jedis.auth("123");
    // 选择数据库(Redis支持多个库,默认是0号库)
    jedis.select(0);
}

这里的Jedis连接配置,其实就是告诉程序:你要连哪个Redis服务器(远程还是本地)、用什么端口、密码是多少,以及选哪个数据库。如果是本地连接,区别就在于主机地址(host)不同。


Jedis操作Redis的例子

接下来看几个实际操作Redis的例子。 **1. 操作字符串** 比如,我们可以用`set`和`get`方法来存取数据:

csharp 复制代码
void testString() {
    String result = jedis.set("name", "zzh"); // 存入键值对
    System.out.println("result=" + result);   // 打印结果
    String name = jedis.get("name");         // 获取键对应的值
    System.out.println("name=" + name);      // 打印值
}

这里,jedis.set就相当于在Redis命令行里执行SET name zzh,而jedis.get就是GET name。本质上,Jedis就是把命令行的操作封装成了代码。 2. 操作哈希表 再比如,我们可以用hsethgetall来操作哈希表:

javascript 复制代码
void testHash() {
    // 插入哈希表数据
    jedis.hset("user:1", "name", "张智昊");
    jedis.hset("user:1", "age", "18");
    // 获取整个哈希表
    Map<String, String> hgetAll = jedis.hgetAll("user:1");
    System.out.println(hgetAll); // 打印结果
}

运行后,你会看到类似这样的输出:

ini 复制代码
{name=张智昊, age=18}

Jedis的局限性

虽然`Jedis`用起来挺方便,但它也有点麻烦的地方。比如,它的方法参数要求很严格,必须是固定的格式。我们来看一下源码:

kotlin 复制代码
public String set(String key, String value) {
    this.checkIsInMultiOrPipeline();
    this.client.set(key, value);
    return this.client.getStatusCodeReply();
}
public String set(String key, String value, SetParams params) {
    this.checkIsInMultiOrPipeline();
    this.client.set(key, value, params);
    return this.client.getStatusCodeReply();
}

从源码可以看出,set方法只能接受keyvalue都是字符串类型的参数。如果你想插入类似user:{"name":"zzh"}这种结构化的数据,Jedis是不支持的,得自己想办法绕过去。 同理,get方法也要求传入的key必须是字符串类型:

kotlin 复制代码
public String get(String key) {
    this.checkIsInMultiOrPipeline();
    this.client.get(key);
    return this.client.getBulkReply();
}

总结一下,Jedis的用法其实跟直接在Redis命令行里敲命令差不多,但因为它的参数限制比较死板,所以有时候会显得有点麻烦。


总结

总的来说,`Jedis`就是一个能让我们用Java代码操作Redis的工具。它的好处是简单直接,缺点是灵活性不够,尤其是处理复杂数据结构时会有点别扭。至于`Spring-Redis`,它在`Jedis`的基础上做了更多封装,后面我会继续学习并分享心得。


spring-redis:

我们先来看一下 `spring-redis` 是如何使用的。以下是一个简单的代码示例,展示了如何通过 `RedisTemplate` 操作 Redis 中的字符串类型数据:

csharp 复制代码
void TestString() {
    // 使用 opsForValue() 方法设置键值对
    redisTemplate.opsForValue().set("name:", "张智昊");
    
    // 通过键获取对应的值
    Object name = redisTemplate.opsForValue().get("name:");
    
    // 输出获取到的值
    System.out.println("name=" + name);
}

从这段代码中可以看出,Spring 框架内置了一个名为 StringRedisTemplate 的工具类,其中提供了 opsForValue() 方法。通过这个方法,我们可以选择操作 Redis 中的数据类型。例如,上面的代码演示了如何对字符串类型的数据进行存储和获取(键值对形式)。

默认序列化机制的问题

然而,默认情况下,`RedisTemplate` 使用的是 Spring 自带的序列化机制(通常是基于 Java 的序列化方式)。这种序列化方式会将数据以二进制的形式存储到 Redis 中。例如,当我们插入键值对 `"name:" -> "张智昊"` 时,实际存储到 Redis 中的数据可能如下所示:

复制代码
\xac\xed\x00\x05t\x00\x09\xe5\xbc\xa0\xe6\x99\xba\xe6\x98\x8a

可以看到,这段数据是经过序列化后的二进制内容,虽然它能够被程序正确解析,但对于人类来说几乎是不可读的。这种默认的序列化机制可能会带来以下几个问题:

  1. 可读性差:由于数据是以二进制形式存储的,直接查看 Redis 数据库的内容时无法直观理解其含义。
  2. 兼容性问题:如果其他语言或框架需要与 Redis 进行交互,它们可能无法解析这种特定的序列化格式。
  3. 调试困难:在开发和调试过程中,查看 Redis 数据变得复杂,增加了排查问题的难度。
解决方案:自定义序列化机制

为了解决上述问题,我们可以通过自定义 `RedisTemplate` 的序列化器来改进数据存储的方式。例如,可以使用 `StringRedisSerializer` 或 `Jackson2JsonRedisSerializer` 等更友好的序列化方式,使存储的数据更加清晰易读。 以下是配置 `RedisTemplate` 使用 `StringRedisSerializer` 的示例代码:

arduino 复制代码
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    
    // 设置 key 和 value 的序列化器
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new StringRedisSerializer());
    
    // 设置连接工厂
    template.setConnectionFactory(redisConnectionFactory);
    
    return template;
}

通过这种方式,存储到 Redis 中的数据将变为更直观的字符串形式。例如,插入 "name:" -> "张智昊" 后,Redis 中的实际存储内容将直接显示为:

arduino 复制代码
"张智昊"

这不仅提高了数据的可读性,还增强了与其他系统的兼容性,同时也便于开发人员进行调试和维护。

3:数据结构于算法

时间复杂度:

主要是学习了一下时间复杂度分析,这算算法的基础吧,毕竟算法就是如何用更短的时间更小的内存去实现同样的事情

我感觉学到的主要就是你去关注那个带n的式子常数级别的运算,对整个程序影响不大,一旦牵扯到n就提高一个层次:

大 O 复杂度表示法:

**1. 只关注循环执行次数最多的一段代码**

我刚才说了,大 O 这种复杂度表示方法只是表示一种变化趋势。我们通常会忽略掉公式中的常量、低阶、系数,只需要记录一个最大阶的量级就可以了。所以, 我们在分析一个算法、一段代码的时间复杂度的时候,也只关注循环执行次数最多的那一段代码就可以了 。这段核心代码执行次数的 n 的量级,就是整段要分析代码的时间复杂度。

2. 加法法则:总复杂度等于量级最大的那段代码的复杂度

总的时间复杂度 * *等于量级最大的那段代码的时间复杂度。那我们将这个规律抽象成公式就是:

如果 T1(n)=O(f(n)),T2(n)=O(g(n));那么 T(n)=T1(n)+T2(n)=max(O(f(n)), O(g(n))) =O(max(f(n), g(n))).

3. 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

我刚讲了一个复杂度分析中的加法法则,这儿还有一个 乘法法则 。类比一下,你应该能"猜到"公式是什么样子的吧?

如果 T1(n)=O(f(n)),T2(n)=O(g(n));那么 T(n)=T1(n)T2(n)=O(f(n)) O(g(n))=O(f(n)*g(n)).

也就是说,假设 T1(n) = O(n),T2(n) = O(n2),则 T1(n) * T2(n) = O(n3)。

空间复杂度分析

前面,咱们花了很长时间讲大 O 表示法和时间复杂度分析,理解了前面讲的内容,空间复杂度分析方法学起来就非常简单了。

前面我讲过,时间复杂度的全称是 渐进时间复杂度表示算法的执行时间与数据规模之间的增长关系 。类比一下,空间复杂度全称就是 渐进空间复杂度 (asymptotic space complexity), 表示算法的存储空间与数据规模之间的增长关系

我还是拿具体的例子来给你说明。(这段代码有点"傻",一般没人会这么写,我这么写只是为了方便给你解释。)

css 复制代码
void print(int n) {
​
  int i = 0;
​
  int[] a = new int[n];
​
  for (i; i <n; ++i) {
​
    a[i] = i * i;
​
  }
​
 
​
  for (i = n-1; i >= 0; --i) {
​
    print out a[i]
​
  }
​
}
复制代码

跟时间复杂度分析一样,我们可以看到,第 2 行代码中,我们申请了一个空间存储变量 i,但是它是常量阶的,跟数据规模 n 没有关系,所以我们可以忽略。第 3 行申请了一个大小为 n 的 int 类型数组,除此之外,剩下的代码都没有占用更多的空间,所以整段代码的空间复杂度就是 O(n)。

我们常见的空间复杂度就是 O(1)、O(n)、O(n2 ),像 O(logn)、O(nlogn) 这样的对数阶复杂度平时都用不到。而且,空间复杂度分析比时间复杂度分析要简单很多。所以,对于空间复杂度,掌握刚我说的这些内容已经足够了。

相关推荐
uzong21 分钟前
技术故障复盘模版
后端
GetcharZp1 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程1 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研1 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi2 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国3 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy3 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack3 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9654 小时前
pip install 已经不再安全
后端
寻月隐君4 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github