redis-05 五大基本类型之List
redis-05 五大基本类型之List
概述
Redis 中的 List 可以存储一个有序的字符串(存储的内容只能是字符串!)列表,由于可以对列表同时进行操作,这使得它同时具备了 stack 和 queue 的特性。
Redis 中的列表类型是通过双向链表实现的,所以查和改两端快,中间慢。
Redis 的集合数据类型都不支持嵌套,列表里面不能存储列表等其它元素,集合里面不能存储列表而只能存储字符串。
命令
增、删
- lpush
- 在链表左侧添加一个元素
- rpop
- 弹出链表右侧的一个元素
@Test
public void pushAndPop() {
long len = j.lpush("numbers", "1", "2", "3");
assertEquals(3L, len);
// 3, 2, 1
len = j.rpush("numbers", "4", "5", "6");
assertEquals(6L, len);
// 3, 2, 1, 4, 5, 6
String number = j.lpop("numbers");
assertEquals("3", number);
number = j.rpop("numbers");
assertEquals("6", number);
}
截取链表
- llen
- 获取链表长度
- lrange
- 截取指定位置的链表
- lrem
- 移出指定个数的指定值的链表元素
@Test
public void subListAndLen() {
long len = j.lpush("numbers", "1", "2", "3");
assertEquals(3L, len);
// 3, 2, 1
len = j.rpush("numbers", "4", "5", "6");
assertEquals(6L, len);
// 3, 2, 1, 4, 5, 6
len = j.llen("numbers");
assertEquals(6L, len);
List<String> numbers = j.lrange("numbers", 1L, 4L);
assertEquals(4, numbers.size());
assertEquals("2", numbers.get(0));
// 顺序永远只能是从左向右
List<String> numbers1 = j.lrange("numbers", -3L, -1L);
// 3, 5, 6
assertEquals("4", numbers1.get(0));
// 当第二个参数 count > 0 时,删除所有匹配元素,负数和正数则会分别
// 从左到右或者从右到左删除指定个匹配的元素
long effected = j.lrem("numbers", 0, "1");
assertEquals(1L, effected);
}
链表倒腾
- rpoplpush
- 顾名思义
- 弹一个、加一个
@Test
public void listElementTransfer() {
long len = j.lpush("numbers", "1", "2", "3");
assertEquals(3L, len);
// 3, 2, 1
len = j.rpush("numbers", "4", "5", "6");
assertEquals(6L, len);
// 3, 2, 1, 4, 5, 6
String number = j.rpoplpush("numbers", "newNumbers");
assertEquals("6", number);
number = j.rpoplpush("numbers", "newNumbers");
assertEquals("5", number);
List<String> newNumbers = j.lrange("newNumbers", 0, -1);
assertNotNull(newNumbers);
assertEquals(2, newNumbers.size());
assertEquals("5", newNumbers.get(0));
assertEquals("6", newNumbers.get(1));
// You can also use this command to move the last element to the front of list in the same list
}
按址操作
按址改值
- lindex
- 获取指定下标的元素
- 下标越界返回 nil(null)
- lset
- 设置指定下标的元素的值
- 下标越界抛出异常
@Test
public void getAndSetListValueByIndex() {
long len = j.lpush("numbers", "1", "2", "3");
assertEquals(3L, len);
// 3, 2, 1
len = j.rpush("numbers", "4", "5", "6");
assertEquals(6L, len);
// 3, 2, 1, 4, 5, 6
String number_2 = j.lindex("numbers", 1L);
assertEquals("2", number_2);
// 下标越界会返回 null
String number_7 = j.lindex("numbers", 7L);
assertNull(number_7);
String status = j.lset("numbers", 4L, "55");
assertEquals("OK", status);
String number_4 = j.lindex("numbers", 4L);
assertEquals("55", number_4);
try {
number_7 = j.lset("numbers", 7L, "77");
assertNull(number_7);
} catch (JedisDataException e) {
System.err.println(e);
}
}
// 执行结果
// redis.clients.jedis.exceptions.JedisDataException: ERR index out of range
trim 操作
- trim
- 截取制定位置的链表元素
- 原链表发生改变
@Test
public void lTrim() {
long len = j.lpush("numbers", "1", "2", "3");
assertEquals(3L, len);
// 3, 2, 1
len = j.rpush("numbers", "4", "5", "6");
assertEquals(6L, len);
// 3, 2, 1, 4, 5, 6
String status = j.ltrim("numbers", 1L, 4L);
// 2, 1, 4, 5
assertEquals("OK", status);
List<String> numbers = j.lrange("numbers", 0, -1);
assertNotNull(numbers);
assertEquals(4, numbers.size());
assertEquals("2", numbers.get(0));
assertEquals("1", numbers.get(1));
assertEquals("4", numbers.get(2));
assertEquals("5", numbers.get(3));
}
按值操作
链表插入
- linsert
- 指定值的指定位置插入热定值
@Test
public void insertIntoList() {
long len = j.lpush("numbers", "1", "2", "3");
assertEquals(3L, len);
// 3, 2, 1
len = j.rpush("numbers", "4", "5", "6");
assertEquals(6L, len);
// 3, 2, 1, 4, 5, 6
len = j.linsert("numbers", ListPosition.AFTER, "2", "8");
assertEquals(7L, len);
// 3, 2, 8, 1, 4, 5, 6
len = j.linsert("numbers", ListPosition.BEFORE, "1", "7");
assertEquals(8L, len);
String number_3 = j.lindex("numbers", 3);
assertEquals("7", number_3);
}
实践
存储文章 ID 列表
我们使用一个 posts:list 来记录文章 ID 列表。这样一来分页操作就变得非常简单了(在这里不做演示)。
存储文章评论
由于评论一般不具备可修改性,所以能直接将评论对象序列化后存储在对应的文章评论列表中。