当前位置:网站首页>系统性详解Redis操作Hash类型数据(带源码分析及测试结果)
系统性详解Redis操作Hash类型数据(带源码分析及测试结果)
2022-07-06 11:32:00 【天然玩家】
1 缘起
系统讲解Redis的Hash类型CURD,
帮助学习者系统且准确学习Hash数据操作,
逐步养成测试的好习惯,
本文较长,Hash的操作比较多,请耐心看,
既可以集中时间看,亦可以碎片时间学习。
特别声明:
(1)文末附全部测试代码;
(2)本篇文章将学习使用如下函数(方法):
序号 | 操作 | method |
---|---|---|
1 | 新增 | hset,hmset,hsetnx |
2 | 删除 | hdel |
3 | 修改 | hset,hmset,hincr,hincrBy,hincrByFloat |
4 | 查询 | hget,hmget,hgetAll,hkeys,hvals,hexists |
(3) 依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.5.1</version>
</dependency>
(4)关于几个术语:Redis数据类型hash,使用时,有三个概念:key,field,value,
其中,key为hash的变量名,field即HashMap中的键,value即HashMap中的值,后文,如为特别声明,
使用键则指HashMap中的键。
2 Hash类型操作
Redis中的hash类型数据,对标Java的HashMap,存储键值对,但是Redis中的hash的值只能为字符串类型,rehash于HashMap也不同。
Redis的hash为了提高性能,使用了渐进式rehash。
2.1 连接池
private static JedisPool getJedisPool() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// Jedis池:最大连接数
jedisPoolConfig.setMaxTotal(1);
// Jedis池:最大空闲连接数
jedisPoolConfig.setMaxIdle(10);
// Jedis池:等待时间
jedisPoolConfig.setMaxWaitMillis(3000);
// Jedis池:连接Redis超时时间
int connectTimeout = 2000;
String redisHost = "127.0.0.1";
int redisPort = 6379;
String redisPassword = "123456";
int redisDb = 0;
// 创建连接池
return new JedisPool(jedisPoolConfig, redisHost, redisPort, connectTimeout, redisPassword, redisDb);
}
2.2 插入数据
插入数据有3种方式:单条插入、多条插入、条件插入。
2.2.1 单条插入:hset
第一次插入不存在的数据,成功写入后返回1,如果第二次修改数据,成功修改,返回0,
所以,不能以hset的返回数据判断是否写入或修改成功,
每次只能插入一条数据,
测试代码段如下:
/** * 单条插入数据 */
@Test
public void insertData() {
try (Jedis jedis = getJedisPool().getResource()) {
Long res1 = jedis.hset("userScore", "xiaoyi", "19");
Long res2 = jedis.hset("userScore", "xiaoer", "20");
Long res3 = jedis.hset("userScore", "xiaosan", "20");
logger.info(">>>>>>>>Redis插入Hash数据:{}, {}, {}", res1, res2, res3);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis插入Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
测试结果如下图所示。
数据形式如下图所示,查询数据时,根据userScore定位到该Hash,然后取对应键的值。
hset源码如下图所示,由注释可知,hset既会创建新数据(Redis不能存在),又会更新数据(Redis存在旧数据)。
位置:redis.clients.jedis.Jedis#hset(java.lang.String, java.lang.String, java.lang.String)
2.2.2 批量插入:hmset
上面hset只能插入单条数据,
有没有可以一次性插入多条的方法呢?
有的,hmset即可一次性插入多条,直接将Map插入Redis,
测试代码段如下:
/** * 批量插入数据 */
@Test
public void insertDataBatch() {
try (Jedis jedis = getJedisPool().getResource()) {
Map<String, String> userScore = new HashMap<>();
userScore.put("xiaosi", "22");
userScore.put("xiaowu", "20");
String res1 = jedis.hmset("userScore", userScore);
logger.info(">>>>>>>>Redis插入Hash数据:{}", res1);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis插入Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
由测试结果可知,成功插入返回:OK。
新增的数据如下图所示:
hmset源码如下图所示,由注释可知,hash中没有数据则新建,hmset有数据,则会替换旧值。
位置:redis.clients.jedis.Jedis#hmset
2.2.3 插入不存在的数据:hsetnx
如果只需要在Hash中添加新数据,旧数据保持不变,怎么办?
hsetnx即满足这个需求,
只增加不存在的数据,
测试代码段如下:
/** * 条件插入数据: * Redis不存在该属性插入,否则不插入 */
@Test
public void insertDataIfNotExist() {
try (Jedis jedis = getJedisPool().getResource()) {
// ("xiaoyi", "19")已存在,不会插入,也不会更新旧数据
Long res1 = jedis.hsetnx("userScore", "xiaoyi", "10");
// ("xiaoliu", "10")不存在,会插入
Long res2 = jedis.hsetnx("userScore", "xiaoliu", "10");
logger.info(">>>>>>>>Redis插入Hash数据:{}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis插入Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
测试结果如下图所示,由结果可知,0表示已经存在的数据,1表示新增的数据,
在hsetnx中,0表示已存在数据,没有更新;1表示:新增数据。
源码如下图所示,由源码可知,hsetnx只变更不存在的数据。
位置:redis.clients.jedis.Jedis#hsetnx
2.3 删除数据:hdel
删除数据,对于hash类型,该版本的Redis库提供了一个hdel方法,
支持删除一条和多条,
测试代码段如下:
/** * 删除数据 */
@Test
public void deleteData() {
try (Jedis jedis = getJedisPool().getResource()) {
Long res1 = jedis.hdel("userScore", "xiaoyi");
Long res2 = jedis.hdel("userScore", "xiaoer", "xiaosan");
logger.info(">>>>>>>>Redis删除Hash数据:{}, {}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis删除Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
测试结果如下图所示,hdel返回的数据是成功删除的数据个数,
如果返回0,则表示,没有删除数据。
hdel源码如下图所示,由注释可知,时间复杂度为O(1)。
位置:redis.clients.jedis.Jedis#hdel
2.4 修改数据
修改数据有3种:直接修改内容、根据整数步长变更纯数字值、根据浮点步长变更纯数字值。
2.4.1 直接修改:hset
hset是新增和修改复用的函数(方法),上面新增数据时以讲解,这里不多讲,只给出测试代码段和测试结果。
/** * 编辑数据:直接修改 */
@Test
public void editData() {
try (Jedis jedis = getJedisPool().getResource()) {
Long res1 = jedis.hset("userScore", "xiaoyi", "6");
logger.info(">>>>>>>>Redis修改Hash数据:{}", res1);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis修改Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
测试结果如下图所示。
2.4.2 指定整数步长修改:incrBy
这个功能很有意思,可以根据整数步长变更数据的值,
特别说明:正数增加,负数减少。
测试代码段如下:
/** * 以指定整数步长更新数值型, * 正数:增加 * 负数:减少 */
@Test
public void increaseByData() {
try (Jedis jedis = getJedisPool().getResource()) {
Long res1 = jedis.hincrBy("userScore", "xiaoyi", 2);
Long res2 = jedis.hincrBy("userScore", "xiaoyi", -5);
logger.info(">>>>>>>>Redis修改Hash数据:{}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis修改Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
hincrBy源码如下图所示,由注释可知,如果增加的键不存在,则会新建数据,同时支持增加和减少两种功能。
位置:redis.clients.jedis.Jedis#hincrBy
2.4.3 指定浮点步长修改:incrByFloat
根据浮点步长变更数据,顾名思义,使用浮点数作为变更的单位。
特别说明:正数增加,负数减少。
测试代码段如下:
/** * 以指定浮点数步长更新数值型, * 正数:增加 * 负数:减少 */
@Test
public void increaseByFloatData() {
try (Jedis jedis = getJedisPool().getResource()) {
Double res1 = jedis.hincrByFloat("userScore", "xiaoyi", 2.0);
Double res2 = jedis.hincrByFloat("userScore", "xiaoyi", -2.0);
logger.info(">>>>>>>>Redis修改Hash数据:{}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis修改Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
hincrByFloat源码如下,由注释可知,处理浮点数据,不存在数据则新建数据。
位置:redis.clients.jedis.Jedis#hincrByFloat
2.5 查询数据
hash数据类型是键值对,因此,数据查询也是五花八门,
如,查询所有数据(键+值)、查询所有键(keys)、查询所有值(values)等。
2.5.1 查询所有数据:hgetAll
查询所有数据,如果机器内存够用或者不影响业务,可以直接查询所有数据,
放入代码中的数据结构Map中做代码缓存,
测试代码段如下:
/** * 查询所有数据 */
@Test
public void queryDataAll() {
try (Jedis jedis = getJedisPool().getResource()) {
Map<String, String> res = jedis.hgetAll("userScore");
logger.info(">>>>>>>>Redis查询Hash数据:{}", res);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
测试结果如下图所示,查询出所有结果。
hgetAll源码如下图所示,由注释可知,查询所有的hash数据。
位置:redis.clients.jedis.Jedis#hgetAll
2.5.2 查询一条数据:hget
查询单条数据,使用hget,指定键的值,
测试代码段如下:
/** * 查询单条数据 */
@Test
public void queryDataOne() {
try (Jedis jedis = getJedisPool().getResource()) {
String res1 = jedis.hget("userScore", "xiaoyi");
String res2 = jedis.hget("userScore", "xio");
logger.info(">>>>>>>>Redis查询Hash数据:{}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
测试结果如下图所示,这里提一下,如果查询的键不存在,则返回null。
hget源码如下,由注释可知,对于不存在的键返回null值。
位置:redis.clients.jedis.Jedis#hget
2.5.3 批量查询:hmget
有时我们需要查询指定范围内数据,
数据量过多,无法一次性查询全部数据,
因此,可以使用批量查询功能:hmget,
测试代码段如下:
/** * 批量查询数据:指定属性 */
@Test
public void queryDataBatch() {
try (Jedis jedis = getJedisPool().getResource()) {
List<String> res1 = jedis.hmget("userScore", "xiaoyi", "xiaoer");
logger.info(">>>>>>>>Redis查询Hash数据:{}", res1);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
测试结果如下图所示,
hmget源码如下图所示,由注释可知,查询过程,遇到不存在的数据,返回null。
位置:redis.clients.jedis.Jedis#hmget
2.5.4 查询所有键名:hkeys
对于判断的业务场景,可以只遍历查询是否存在某个键,
一来,只查键数据量与全部数据(键+值)相比,会减少许多;
二来,查询速度也会提高;
所以,有了,只查hash的键:hkeys,
测试代码段如下:
/** * 查询所有属性(HashMap中的键) */
@Test
public void queryDataAllKeys() {
try (Jedis jedis = getJedisPool().getResource()) {
Set<String> res = jedis.hkeys("userScore");
logger.info(">>>>>>>>Redis查询Hash数据:{}", res);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
测试结果如下图所示,返回键的列表。
hkeys源码如下图所示,由注释可知,返回hash所有的属性名称。
位置:redis.clients.jedis.Jedis#hkeys
2.5.5 查询所有值:hvals
前面有了只查询键,那么,如果有需要只查询值的场景,怎么办?
当然,有方法:hvals,
提供只查询值的功能,满足只需要根据值来做业务或者相关计算,
测试代码段如下:
/** * 查询所有值 */
@Test
public void queryDataAllValues() {
try (Jedis jedis = getJedisPool().getResource()) {
List<String> res = jedis.hvals("userScore");
logger.info(">>>>>>>>Redis查询Hash数据:{}", res);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
测试结果如下图所示,由结果可知,返回的数据为列表类型。
hvals源码如下图所示,由注释可知,返回hash所有值。
位置:redis.clients.jedis.Jedis#hvals
2.5.6 查询数据个数:hlen
有时,我们需要根据插入数据的数量来判断是否将数据全部同步到Redis,
或者,需要检查hash数据量,
这里就需要使用hlen,查询当前某个hash的数据量,
测试代码段如下:
/** * 查询Hash中数据条数 */
@Test
public void queryDataLength() {
try (Jedis jedis = getJedisPool().getResource()) {
Long res = jedis.hlen("userScore");
logger.info(">>>>>>>>Redis查询Hash数据:{}", res);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
测试结果如下图所示,由结果可知,当前hash,userScore存储了4条数据。
hlen源码如下图所示,如果查询的hash没有数据或者没有hash则返回0。
位置:redis.clients.jedis.Jedis#hlen
2.5.7 查询数据是否存在:hexists
这是一个点查功能,判断指定的某个键是否存在,
无需使用临时变量存储,
直接条件判断即可,因为,hexists返回的直接是布尔值,
测试代码段如下:
/** * 查询某个属性是否存在 */
@Test
public void queryDataExistsOrNot() {
try (Jedis jedis = getJedisPool().getResource()) {
Boolean res1 = jedis.hexists("userScore", "xiaoyi");
Boolean res2 = jedis.hexists("userScore", "xiao");
logger.info(">>>>>>>>Redis查询Hash数据:{}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
测试结果如下图所示,存在则返回true,不存在则返回false。
hexists源码如下图所示,由注释可知,判断hash键(属性)是否存在。
位置:redis.clients.jedis.Jedis#hexists
3 小结
核心:
(1)增加数据:
单条新增:直接新增:hset,key不存在则新增,key存在则覆盖旧值;
单条新增:条件新增:hsetnx,key不存在则新增,key存在不操作;
批量新增:hmset;
(2)删除数据:可以单条也可批量删除;
(3)修改:修改数据使用hset或者hmset,直接覆盖旧值。如果需要增加或减少数据,可以使用纯数字的数据,指定步长加或减,支持整数步长和浮点步长,通过步长的正负进行加减;
(4)查询数据:单条查询(hget),返回单条数据;多条查询(hmget),返回列表数据,查询所有数据、所有键(属性)、所有值。
序号 | 操作 | method |
---|---|---|
1 | 新增 | hset,hmset,hsetnx |
2 | 删除 | hdel |
3 | 修改 | hset,hmset,hincr,hincrBy,hincrByFloat |
4 | 查询 | hget,hmget,hgetAll,hkeys,hvals,hexists |
附件
完整测试样例
package database_test.redis_test;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** * Hash数据类型操作. * * @author xindaqi * @since 2022-07-03 9:12 */
public class HashTest {
private static final Logger logger = LoggerFactory.getLogger(HashTest.class);
private static JedisPool getJedisPool() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// Jedis池:最大连接数
jedisPoolConfig.setMaxTotal(1);
// Jedis池:最大空闲连接数
jedisPoolConfig.setMaxIdle(10);
// Jedis池:等待时间
jedisPoolConfig.setMaxWaitMillis(3000);
// Jedis池:连接Redis超时时间
int connectTimeout = 2000;
String redisHost = "127.0.0.1";
int redisPort = 6379;
String redisPassword = "123456";
int redisDb = 0;
// 创建连接池
return new JedisPool(jedisPoolConfig, redisHost, redisPort, connectTimeout, redisPassword, redisDb);
}
/** * 单条插入数据 */
@Test
public void insertData() {
try (Jedis jedis = getJedisPool().getResource()) {
Long res1 = jedis.hset("userScore", "xiaoyi", "66");
Long res2 = jedis.hset("userScore", "xiaoer", "20");
Long res3 = jedis.hset("userScore", "xiaosan", "20");
logger.info(">>>>>>>>Redis插入Hash数据:{}, {}, {}", res1, res2, res3);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis插入Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 批量插入数据 */
@Test
public void insertDataBatch() {
try (Jedis jedis = getJedisPool().getResource()) {
Map<String, String> userScore = new HashMap<>();
userScore.put("xiaosi", "22");
userScore.put("xiaowu", "20");
String res1 = jedis.hmset("userScore", userScore);
logger.info(">>>>>>>>Redis插入Hash数据:{}", res1);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis插入Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 条件插入数据: * Redis不存在该属性插入,否则不插入 */
@Test
public void insertDataIfNotExist() {
try (Jedis jedis = getJedisPool().getResource()) {
// ("xiaoyi", "19")已存在,不会插入,也不会更新旧数据
Long res1 = jedis.hsetnx("userScore", "xiaoyi", "10");
// ("xiaoliu", "10")不存在,会插入
Long res2 = jedis.hsetnx("userScore", "xiaoliu", "10");
logger.info(">>>>>>>>Redis插入Hash数据:{}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis插入Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 删除数据 */
@Test
public void deleteData() {
try (Jedis jedis = getJedisPool().getResource()) {
Long res1 = jedis.hdel("userScore", "xiaoyi");
Long res2 = jedis.hdel("userScore", "xiaoer", "xiaosan");
logger.info(">>>>>>>>Redis删除Hash数据:{}, {}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis删除Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 编辑数据:直接修改 */
@Test
public void editData() {
try (Jedis jedis = getJedisPool().getResource()) {
Long res1 = jedis.hset("userScore", "xiaoyi", "6");
logger.info(">>>>>>>>Redis修改Hash数据:{}", res1);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis修改Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 以指定整数步长更新数值型, * 正数:增加 * 负数:减少 */
@Test
public void increaseByData() {
try (Jedis jedis = getJedisPool().getResource()) {
Long res1 = jedis.hincrBy("userScore", "xiaoyi", 2);
Long res2 = jedis.hincrBy("userScore", "xiaoyi", -5);
logger.info(">>>>>>>>Redis修改Hash数据:{}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis修改Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 以指定浮点数步长更新数值型, * 正数:增加 * 负数:减少 */
@Test
public void increaseByFloatData() {
try (Jedis jedis = getJedisPool().getResource()) {
Double res1 = jedis.hincrByFloat("userScore", "xiaoyi", 2.0);
Double res2 = jedis.hincrByFloat("userScore", "xiaoyi", -2.0);
logger.info(">>>>>>>>Redis修改Hash数据:{}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis修改Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 查询所有数据 */
@Test
public void queryDataAll() {
try (Jedis jedis = getJedisPool().getResource()) {
Map<String, String> res = jedis.hgetAll("userScore");
logger.info(">>>>>>>>Redis查询Hash数据:{}", res);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 查询单条数据 */
@Test
public void queryDataOne() {
try (Jedis jedis = getJedisPool().getResource()) {
String res1 = jedis.hget("userScore", "xiaoyi");
String res2 = jedis.hget("userScore", "xio");
logger.info(">>>>>>>>Redis查询Hash数据:{}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 批量查询数据:指定属性 */
@Test
public void queryDataBatch() {
try (Jedis jedis = getJedisPool().getResource()) {
List<String> res1 = jedis.hmget("userScore", "xiaoyi", "xiaoer");
logger.info(">>>>>>>>Redis查询Hash数据:{}", res1);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 查询所有属性(HashMap中的键) */
@Test
public void queryDataAllKeys() {
try (Jedis jedis = getJedisPool().getResource()) {
Set<String> res = jedis.hkeys("userScore");
logger.info(">>>>>>>>Redis查询Hash数据:{}", res);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 查询所有值 */
@Test
public void queryDataAllValues() {
try (Jedis jedis = getJedisPool().getResource()) {
List<String> res = jedis.hvals("userScore");
logger.info(">>>>>>>>Redis查询Hash数据:{}", res);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 查询Hash中数据条数 */
@Test
public void queryDataLength() {
try (Jedis jedis = getJedisPool().getResource()) {
Long res = jedis.hlen("userScore");
logger.info(">>>>>>>>Redis查询Hash数据:{}", res);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
/** * 查询某个属性是否存在 */
@Test
public void queryDataExistsOrNot() {
try (Jedis jedis = getJedisPool().getResource()) {
Boolean res1 = jedis.hexists("userScore", "xiaoyi");
Boolean res2 = jedis.hexists("userScore", "xiao");
logger.info(">>>>>>>>Redis查询Hash数据:{}, {}", res1, res2);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis查询Hash数据异常:", ex);
throw new RuntimeException(ex);
}
}
}
边栏推荐
- R语言使用dt函数生成t分布密度函数数据、使用plot函数可视化t分布密度函数数据(t Distribution)
- Php+redis realizes the function of canceling orders over time
- Benefit a lot, Android interview questions
- 第五期个人能力认证考核通过名单公布
- Druid database connection pool details
- 关于图像的读取及处理等
- Qlabel marquee text display
- The list of people who passed the fifth phase of personal ability certification assessment was published
- PMP practice once a day | don't get lost in the exam -7.6
- A full set of teaching materials, real questions of Android interview of 7 major manufacturers including Alibaba Kwai pinduoduo
猜你喜欢
Meilu biological IPO was terminated: the annual revenue was 385million, and Chen Lin was the actual controller
Mind map + source code + Notes + project, ByteDance + JD +360+ Netease interview question sorting
It's super detailed in history. It's too late for you to read this information if you want to find a job
提前解锁 2 大直播主题!今天手把手教你如何完成软件包集成?|第 29-30 期
通俗的讲解,带你入门协程
Graffiti intelligence is listed on the dual main board in Hong Kong: market value of 11.2 billion Hong Kong, with an annual revenue of 300 million US dollars
CCNP Part 11 BGP (III) (essence)
AutoCAD - what is the default lineweight for centerline drawing and CAD? Can I modify it?
ROS自定义消息发布订阅示例
LeetCode-1279. Traffic light intersection
随机推荐
Yutai micro rushes to the scientific innovation board: Huawei and Xiaomi fund are shareholders to raise 1.3 billion
五金机电行业智能供应链管理系统解决方案:数智化供应链为传统产业“造新血”
About static type, dynamic type, ID, instancetype
Modulenotfounderror: no module named 'PIL' solution
应用使用Druid连接池经常性断链问题分析
The dplyr package of R language performs data grouping aggregation statistical transformations and calculates the grouping mean of dataframe data
MRO industrial products enterprise procurement system: how to refine procurement collaborative management? Industrial products enterprises that want to upgrade must see!
Interface test tool - postman
PMP每日一练 | 考试不迷路-7.6
Use of map (the data of the list is assigned to the form, and the JSON comma separated display assignment)
R语言使用dt函数生成t分布密度函数数据、使用plot函数可视化t分布密度函数数据(t Distribution)
A popular explanation will help you get started
Sanmian ant financial successfully got the offer, and has experience in Android development agency recruitment and interview
USB host driver - UVC swap
How to improve website weight
Analysis of frequent chain breaks in applications using Druid connection pools
How to type multiple spaces when editing CSDN articles
Swagger2 reports an error illegal DefaultValue null for parameter type integer
It's super detailed in history. It's too late for you to read this information if you want to find a job
业务与应用同步发展:应用现代化的策略建议