1. Jedis Lists Demos
对应redis命令参考:redis lists组命令
package samples.cache.jedis;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import redis.clients.jedis.BinaryClient;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisDataException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
/**
* @author Zi Lai Ye
* @date 2017/10/7
*/
public class JedisListTests {
public static final Logger logger = LoggerFactory.getLogger(JedisListTests.class);
public static final String HOST = "localhost";
public static final int PORT = 6379;
public static final String key = "mylist1";
public static final int lsize = 10;
public static final String OK = "OK";
private Jedis jedis;
@Before
public void beforeClass() {
jedis = new Jedis(HOST, PORT);
jedis.flushDB();
}
@After
public void afterClass() {
// jedis.flushDB();
jedis.close();
}
// lpush 将一个或多个值 value 插入到列表 key 的左侧(表头) lpush + lpop 即可实现先进后出的栈操作
@Test
public void lpush() {
// 断言key不存在
Boolean exists = jedis.exists(key);
assertFalse(exists);
// 对一个不存在的 key 进行 lpush 操作, 会先创建一个空列表, 然后再进行 lpush操作
Long pushResult = jedis.lpush(key, "one");
assertEquals(Long.valueOf(1), pushResult);
jedis.lrem(key, 1, "one");
// 依次在列表左侧插入1 2 3 4..., 得到的列表为插入顺序相反列表: ...4 3 2 1
for (int i = 1; i <= lsize; i++) {
jedis.lpush(key, String.valueOf(i));
}
// 判断长度
Long llen = jedis.llen(key);
assertEquals(Long.valueOf(lsize), llen);
// 依次从右侧(即列表尾部)取出全部的列表元素, 判断其顺序是否为1 2 3 4 ...
for (int i = 1; i <= lsize; i++) {
String rpop = jedis.rpop(key);
assertEquals(String.valueOf(i), rpop);
}
// 对非列表类型的 key 进行 lpush 操作, 返回错误
jedis.set("key1", "value1");
try {
jedis.lpush("key1", "value2");
} catch (Exception e) {
logger.error("对非列表类型的 key 进行 lpush 操作, 返回错误", e);
}
}
// lpushx 将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表。
@Test
public void lpushx() {
// key 不存在
Boolean exists = jedis.exists(key);
assertFalse(exists);
// 如果 key 不存在 , lpushx 什么也不做
Long lpushxResult = jedis.lpushx(key, "one");
assertEquals(Long.valueOf(0), lpushxResult);
// 如果 key 类型不是列表, lpushx 返回错误
jedis.set("key1", "v1");
try {
lpushxResult = jedis.lpushx("key1", "v2");
assertEquals(Long.valueOf(0), lpushxResult);
} catch (Exception e) {
logger.error("如果 key 类型不是列表, lpushx 返回错误", e);
}
// 仅当 key 是一个列表时, 操作成功, 返回执行成功后表的长度
jedis.lpush(key, "one");
lpushxResult = jedis.lpushx(key, "two");
assertEquals(Long.valueOf(2), lpushxResult);
}
// lpop 移除并返回列表 key 的头元素。
@Test
public void lpop() {
// 断言 key 不存在
Boolean exists = jedis.exists(key);
assertFalse(exists);
// 如果 key 不存在 , 返回 Null
String lpopResult = jedis.lpop(key);
assertNull(lpopResult);
// 移除并返回列表 key 的头元素
pushBatch();
assertEquals(Long.valueOf(lsize), jedis.llen(key));
String headElel = jedis.lindex(key, 0);
String popElel = jedis.lpop(key);
assertEquals(headElel, popElel); // 头元素为索引为0的元素
assertEquals(Long.valueOf(lsize - 1), jedis.llen(key)); // 数量减1
// 如果 key 类型不是列表, 返回错误信息
jedis.set("key1", "v1");
try {
jedis.lpop("key1");
} catch (Exception e) {
logger.error("如果 key 类型不是列表, 返回错误信息", e);
}
}
// rpush 将一个或多个值 value 插入到列表 key 的表尾(最右边)。
@Test
public void rpush() {
// 断言key不存在
Boolean exists = jedis.exists(key);
assertFalse(exists);
// 对一个不存在的 key 进行 rpush 操作, 会先创建一个空列表, 然后再进行 rpush操作
Long pushResult = jedis.rpush(key, "one");
assertEquals(Long.valueOf(1), pushResult);
jedis.lrem(key, 1, "one");
// 在表尾插入元素
for (int i = 1; i <= lsize; i++) {
jedis.rpush(key, String.valueOf(i));
}
assertEquals(Long.valueOf(lsize), jedis.llen(key));
for (int i = 1; i <= lsize; i++) {
String lpopElem = jedis.lpop(key);
assertEquals(String.valueOf(i), lpopElem);
}
// 对非列表类型的 key 进行 rpush 操作, 返回错误
jedis.set("key1", "value1");
try {
jedis.rpush("key1", "value2");
} catch (Exception e) {
logger.error("对非列表类型的 key 进行 rpush 操作, 返回错误", e);
}
}
// llen 获取列表的长度
@Test
public void llen() {
// 1. 不存在的列表 长度为0
Long llen = jedis.llen(key);
assertEquals(Long.valueOf(0), llen);
logger.info("不存在的key进行llen操作, 被认定为空列表, 返回长度为0");
// 2. 已存在的列表, 返回其长度
pushBatch();
llen = jedis.llen(key);
assertEquals(Long.valueOf(lsize), llen);
// 3. 非列表类型的key,返回错误
jedis.set("key1", "value1");
try {
llen = jedis.llen("key1");
} catch (JedisDataException ex) {
logger.error("对非列表类型的key进行llen操作, 返回错误", ex);
}
}
// 将列表 key 下标为 index 的元素的值设置为 value 。
@Test
public void lset() {
String newValue;
// lset 对不存在的 key 进行操作, 返回错误 , no such key
try {
newValue = jedis.lset(key, 0, "newValue");
} catch (Exception ex) {
logger.error("对不存在的 key 进行操作, 返回错误 ", ex);
}
// lset对已存在的key操作成功, 返回OK
pushBatch();
newValue = jedis.lset(key, 0, "newValue");
assertEquals(OK, newValue);
// 索引超出范围, 报错
try {
jedis.lset(key, lsize, "test");
} catch (Exception e) {
logger.info("超出索引范围, 报错", e);
}
// 类型不是列表, 返回错误
jedis.set("key1", "1");
try {
jedis.lset("key1", 0, "");
} catch (Exception e) {
logger.info("类型不是列表, 返回错误", e);
}
}
// 返回列表 key 中,下标为 index 的元素。
@Test
public void lindex() {
// key 不存在
Boolean exists = jedis.exists(key);
assertFalse(exists);
// key 不存在时, 返回 null
String lindexResult = jedis.lindex(key, 0);
assertNull(lindexResult);
pushBatch();
// 返回下标为index的元素
int revertIndex = lsize * -1;
for (int i = 0; i < lsize; i++) {
assertEquals(String.valueOf(i + 1), jedis.lindex(key, i));
assertEquals(String.valueOf(i + 1), jedis.lindex(key, revertIndex++));
}
// 如果 key 类型不是列表, 返回错误
jedis.set("key1", "v1");
try {
jedis.lindex("key1", 0);
} catch (Exception e) {
logger.error("如果 key 类型不是列表, 返回错误", e);
}
}
// linsert: 将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。
@Test
public void linsert() {
Boolean exists = jedis.exists(key);
assertFalse(exists);
// key 不存在或为空列表, 返回 0
Long linsertResult = jedis.linsert(key, BinaryClient.LIST_POSITION.BEFORE, "1", "0");
assertEquals(Long.valueOf(0), linsertResult);
// key 存在, 将值插入到列表 key 当中, 位于指定值 之前
pushBatch();
assertEquals(Long.valueOf(lsize), jedis.llen(key));
linsertResult = jedis.linsert(key, BinaryClient.LIST_POSITION.BEFORE, "1", "0");
assertEquals(Long.valueOf(lsize + 1), jedis.llen(key)); // 插入成功, 长度+1
assertEquals("0", jedis.lindex(key, 0)); // 第一个元素
// 如果指定值 pivot 不存在, 返回 -1
linsertResult = jedis.linsert(key, BinaryClient.LIST_POSITION.AFTER, "12", "13");
assertEquals(Long.valueOf(-1), linsertResult);
// 如果 key 的类型不是列表, 异常错误
jedis.set("key1", "v1");
try {
jedis.linsert("key1", BinaryClient.LIST_POSITION.AFTER, "1", "2");
} catch (Exception e) {
logger.error("如果 key 的类型不是列表, 异常错误", e);
}
}
// lrem remove。根据参数 count 的值,移除列表中与参数 value 相等的元素。
@Test
public void lrem() {
Boolean exists = jedis.exists(key);
assertFalse(exists);
// 不存在的 key 进行 lrem, 被视为空列表移除, 返回 移除数量 0
Long lremResult = jedis.lrem(key, 0L, "1");
assertEquals(Long.valueOf(0), lremResult);
// 插入 1-10 的列表 两遍
pushBatch();
pushBatch();
// 参数 count > 0 , 从左向右移除count个 value
lremResult = jedis.lrem(key, 1, "1");
assertEquals(Long.valueOf(1), lremResult); // 移除1个
assertEquals("2", jedis.lindex(key, 0)); // 第1个元素变成 2
jedis.del(key);
pushBatch();
pushBatch();
// 参数 count = 0 , 移除列表中所有与value相等的值
lremResult = jedis.lrem(key, 0, "2");
assertEquals(Long.valueOf(2), lremResult); // 移除2个
assertEquals(Long.valueOf(lsize * 2 - 2), jedis.llen(key)); // 数量少了2个
jedis.del(key);
pushBatch();
pushBatch();
// 参数 count < 0, 从右向左移除 count 个 value
lremResult = jedis.lrem(key, -2, "3");
assertEquals(Long.valueOf(2), lremResult);
assertEquals(Long.valueOf(lsize * 2 - 2), jedis.llen(key));
// 如果 key 不是列表类型, 抛出异常
jedis.set("key1", "v1");
try {
jedis.lrem(key, 0, "1");
} catch (Exception e) {
logger.error("如果 key 不是列表类型, 抛出异常", e);
}
}
// ltrim 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
@Test
public void ltrim() {
Boolean exists = jedis.exists(key);
assertFalse(exists);
// 如果 key 不存在, 认为修剪成功, 返回OK
String ltrimResult = jedis.ltrim(key, 0, -1);
assertEquals(OK, ltrimResult);
pushBatch();
// 如果 key 存在, 返回 start-top之间的元素, 其它元素全部移除
ltrimResult = jedis.ltrim(key, 0, 4);
assertEquals(OK, ltrimResult);
assertEquals(Long.valueOf(5), jedis.llen(key));
jedis.del(key);
pushBatch();
// 超出范围的下标不会报错, 如果 start 超范围, 或者 start > stop , 都会把整个列表清空
ltrimResult = jedis.ltrim(key, lsize, 5);
assertEquals(OK, ltrimResult);
assertEquals(Long.valueOf(0), jedis.llen(key));
// 如果 key 类型不是列表, 报错
jedis.set("key1", "v1");
try {
jedis.ltrim("key1", 0, -1);
} catch (Exception e) {
logger.error("如果 key 类型不是列表, 报错", e);
}
}
// blpop 是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,
// 直到等待超时或发现可弹出元素为止。
@Test
public void blpop() throws InterruptedException {
jedis.lpush(key, "one");
// 如果指定了多个 key , 则从左到右依次查找, 找到第一个非空列表, 执行lpop操作, 并将 key -value 返回
List<String> blpopResults = jedis.blpop(20, key, "list0", "list1", "list2");
assertEquals(2, blpopResults.size());
assertEquals(key, blpopResults.get(0));
assertEquals("one", blpopResults.get(1));
assertEquals(Long.valueOf(0), jedis.llen(key));
// 如果指定的多个 key 都不存在或为空列表, 则阻塞指定时间, 等待有新元素或超时为止
new Thread(new Runnable() {
public void run() {
logger.info("开启新线程开始 blpop...");
List<String> blpopResponses = jedis.blpop(10, key, "list0", "list1", "list2");
if (blpopResponses != null && blpopResponses.size() > 0) {
logger.info("blpop 监听到新的元素, lpop key:{} result:{}", blpopResponses.get(0), blpopResponses.get(1));
} else {
logger.info("blpop 阻塞时间到, 超时");
}
}
}).start();
logger.info("在 监听的 key 中 push 一个值...");
jedis.lpush(key, "hello, any one here?");
Thread.sleep(20000L);
}
private void pushBatch() {
for (int i = 1; i <= lsize; i++) {
jedis.rpush(key, String.valueOf(i));
}
}
}