前言

这一篇文章将讲述Redis中的list类型命令,同样也是通过demo来讲述,其他部分这里就不在赘述了。

项目Github地址:https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-list

案例

demo功能是队列,整个demo的大致页面如下。左边是存储到Redis中的数据,右边是从Redis中弹出的数据。

准备工作

首先定义一个存储list的key

private static final String LIST_KEY = "list:1";

队列的key就用list:1

redis操作对象

private RedisTemplate redisTemplate;
//string 命令操作对象
private ValueOperations valueOperations;
//list 命令操作对象
private ListOperations listOperations;

list在Redis中的结构可以看下图(图片来源于Redis in Action)。

插入数据

头部插入

命令介绍
命令 用例 描述
LPUSH LPUSH key value [value ...] 将所有指定的值插入到存于 key 的列表的头部。 如果 key 不存在,那么在进行 push 操作前会创建一个空列表。
LPUSHX LPUSHX key value 只有当 key 已经存在并且存着一个 list 的时候,在这个 key 下面的 list 的头部插入 value。

接下来看看demo中头部插入的功能,点击下图中头部插入按钮,然后在弹出框中填入数字0,点击提交后整个头部插入流程结束。可以看到左边的队列数据出现了一条{"data":"0"} 数据,在数据{"data":"1"} 上面。

来看看后台的方法

@RequestMapping(value = "/leftPop",method = RequestMethod.GET)
public Object leftPop(){
return listOperations.leftPop(LIST_KEY);
}

如果需要在Redis中操作,可以敲下面的命令

lpush list:1 "{\"data\":\"0\"}"

尾部插入

命令介绍
命令 用例 描述
RPUSH RPUSH key value [value ...] 向存于 key 的列表的尾部插入所有指定的值。如果 key 不存在,那么会创建一个空的列表然后再进行 push 操作。
RPUSHX RPUSHX key value 将值 value 插入到列表 key 的表尾, 当且仅当 key 存在并且是一个列表。

接下来看看demo中尾部插入的功能,点击下图中尾部插入按钮,然后在弹出框中填入数字11,点击提交后整个新增流程结束。可以看到左边的队列数据出现了一条{"data":"11"} 数据,在数据{"data":"10"}下面。

来看看后台的方法

@RequestMapping(value = "/rightPop",method = RequestMethod.GET)
public Object rightPop(){
return listOperations.rightPop(LIST_KEY);
}

如果需要在Redis中操作,可以敲下面的命令

rpush list:1 "{\"data\":\"11\"}"

列表查询

命令介绍

同样先看看相关的获取值命令

命令 用例 描述
LRANGE LRANGE key start stop 返回存储在 key 的列表里指定范围内的元素。
LINDEX LINDEX key index 返回列表里的元素的索引 index 存储在 key 里面。
LLEN LLEN key 返回存储在 key 里的list的长度。

后台查询方法,将新增的内容查询出来

@RequestMapping(value = "/getList",method = RequestMethod.GET)
public List getList(){
List list = listOperations.range(LIST_KEY, 0, -1); //可以用size获取成员长度
//listOperations.size(LIST_KEY); return list;
}

数据弹出

头部弹出

命令 用例 描述
LPOP LPOP key 移除并且返回 key 对应的 list 的第一个元素。
BLPOP BLPOP key [key ...] timeout 它是命令 LPOP 的阻塞版本,这是因为当给定列表内没有任何元素可供弹出的时候, 连接将被 BLPOP 命令阻塞。

接下来看看头部弹出的功能,点击下图中头部弹出按钮,可以看到左边的队列顶部数据减少了,在右边弹出的数据出现了左边队列数据消失的数据。

来看看后台的方法

@RequestMapping(value = "/leftPop",method = RequestMethod.GET)
public Object leftPop(){
return listOperations.leftPop(LIST_KEY);
}

如果需要在Redis中操作,可以敲下面的命令

lpop list:1

尾部弹出

命令 用例 描述
RPOP RPOP key 移除并返回存于 key 的 list 的最后一个元素。
BRPOP BRPOP key [key ...] timeout 它是 RPOP 的阻塞版本,因为这个命令会在给定list无法弹出任何元素的时候阻塞连接。

接下来看看尾部弹出的功能,点击下图中尾部弹出按钮,可以看到左边的队列尾部数据减少了,在右边弹出的数据出现了左边队列数据消失的数据。

来看看后台的方法

@RequestMapping(value = "/rightPop",method = RequestMethod.GET)
public Object rightPop(){
return listOperations.rightPop(LIST_KEY);
}

如果需要在Redis中操作,可以敲下面的命令

rpop list:1

其他命令

命令 用例 描述
LINSERT LINSERT key BEFORE|AFTER pivot value 把 value 插入存于 key 的列表中在基准值 pivot 的前面或后面。
LREM LREM key count value 从存于 key 的列表里移除前 count 次出现的值为 value 的元素。
LSET LSET key index value 设置 index 位置的list元素的值为 value。
LTRIM LTRIM key start stop 修剪(trim)一个已存在的 list,这样 list 就会只包含指定范围的指定元素。
RPOPLPUSH RPOPLPUSH source destination 原子性地返回并移除存储在 source 的列表的最后一个元素(列表尾部元素), 并把该元素放入存储在 destination 的列表的第一个元素位置(列表头部)。
BRPOPLPUSH BRPOPLPUSH source destination timeout BRPOPLPUSH 是 RPOPLPUSH 的阻塞版本。

RPOPLPUSH和BRPOPLPUSH

这两个命令作用其实是相同的,只不过BRPOPLPUSH是阻塞的,当没有数据时,会一直阻塞,直到有数据。

在Redis官方文档中,用RPOPLPUSH命令举了两个例子,一个是Reliable queue(安全的队列 ),另一个是Circular list(循环列表)。

Reliable queue(安全的队列 )

Redis通常都被用做一个处理各种后台工作或消息任务的消息服务器。 一个简单的队列模式就是:生产者把消息放入一个列表中,等待消息的消费者用 RPOP 命令(用轮询方式), 或者用 BRPOP 命令(如果客户端使用阻塞操作会更好)来得到这个消息。

然而,因为消息有可能会丢失,所以这种队列并是不安全的。例如,当接收到消息后,出现了网络问题或者消费者端崩溃了, 那么这个消息就丢失了。

RPOPLPUSH (或者其阻塞版本的 BRPOPLPUSH) 提供了一种方法来避免这个问题:消费者端取到消息的同时把该消息放入一个正在处理中的列表。 当消息被处理了之后,该命令会使用 LREM 命令来移除正在处理中列表中的对应消息。

另外,可以添加一个客户端来监控这个正在处理中列表,如果有某些消息已经在这个列表中存在很长时间了(即超过一定的处理时限), 那么这个客户端会把这些超时消息重新加入到队列中。

翻译来自 http://www.redis.cn/commands/rpoplpush.html

Circular list(循环列表)

RPOPLPUSH 命令的 source 和 destination 是相同的话, 那么客户端在访问一个拥有n个元素的列表时,可以在 O(N) 时间里一个接一个获取列表元素, 而不用像 LRANGE 那样需要把整个列表从服务器端传送到客户端。

上面这种模式即使在以下两种情况下照样能很好地工作: * 有多个客户端同时对同一个列表进行旋转(rotating):它们会取得不同的元素,直到列表里所有元素都被访问过,又从头开始这个操作。 * 有其他客户端在往列表末端加入新的元素。

这个模式让我们可以很容易地实现这样一个系统:有 N 个客户端,需要连续不断地对一批元素进行处理,而且处理的过程必须尽可能地快。 一个典型的例子就是服务器上的监控程序:它们需要在尽可能短的时间内,并行地检查一批网站,确保它们的可访问性。

值得注意的是,使用这个模式的客户端是易于扩展(scalable)且安全的(reliable),因为即使客户端把接收到的消息丢失了, 这个消息依然存在于队列中,等下次迭代到它的时候,由其他客户端进行处理。

翻译来自 http://www.redis.cn/commands/rpoplpush.html

案例-约瑟夫问题

约瑟夫问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环

人们站在一个等待被处决的圈子里。 计数从圆圈中的指定点开始,并沿指定方向围绕圆圈进行。 在跳过指定数量的人之后,执行下一个人。 对剩下的人重复该过程,从下一个人开始,朝同一方向跳过相同数量的人,直到只剩下一个人,并被释放。

问题即,给定人数、起点、方向和要跳过的数字,选择初始圆圈中的位置以避免被处决。

来自维基百科 https://zh.wikipedia.org/wiki/约瑟夫斯问题

思路

定义一个list key为josephus,利用

RPOPLPUSH  josephus josephus

命令来构造循环链表,每当数到3时,使用rpop

rpop josephus

命令弹出

代码实现
public class JosephusProblem extends RedisBaseConnection {

    @Test
public void test() {
//构造数据
for (int i = 1; i <= 41; i++) {
listOperations.leftPush("josephus", String.valueOf(i));
} int index = 1;
while (listOperations.size("josephus") > 0) {
//当数到3时,弹出
if (index == 3) {
System.out.println(listOperations.range("josephus", 0, -1));
System.out.println("当前被杀的人是:" + listOperations.rightPop("josephus"));
index = 0;
} else {
listOperations.rightPopAndLeftPush("josephus", "josephus");
}
index++;
}
}
}

整个代码步骤如下

  1. 先是模拟有41个人(向redis中key为josephus的list添加41个数据)
  2. 定义索引index
  3. 循环判断key为josephus的数据长度是否大于0
  4. 当索引index为3时,调用Redis的rpop命令弹出对应的数据。索引index不为3时,调用RPOPLPUSH命令,将对应的数据放到队列头部
  5. 索引index加1

运行结果有点长,这里只截图最后一部分的结果,如下

约瑟夫问题代码请点击JosephusProblem.java


建议学习的人最好每个命令都去敲下,加深印象。下面诗句送给你们。

纸上得来终觉浅,绝知此事要躬行。————出自《冬夜读书示子聿》

redis系列:通过队列案例学习list命令的更多相关文章

  1. redis系列:通过文章点赞排名案例学习sortedset命令

    前言 这一篇文章将讲述Redis中的sortedset类型命令,同样也是通过demo来讲述,其他部分这里就不在赘述了. 项目Github地址:https://github.com/rainbowda/ ...

  2. redis系列:通过共同好友案例学习set命令

    前言 这一篇文章将讲述Redis中的set类型命令,同样也是通过demo来讲述,其他部分这里就不在赘述了. 项目Github地址:https://github.com/rainbowda/learnW ...

  3. redis系列:通过通讯录案例学习hash命令

    前言 这一篇文章将讲述Redis中的hash类型命令,同样也是通过demo来讲述,其他部分这里就不在赘述了. 项目Github地址:https://github.com/rainbowda/learn ...

  4. redis系列:通过日志案例学习string命令

    前言 该文章将通过一个小demo将讲述Redis中的string类型命令.demo将以springboot为后台框架快速开发,iview前端框架进行简单的页面设计,为了方便就不使用DB存储数据了,直接 ...

  5. NoSQL初探之人人都爱Redis:(3)使用Redis作为消息队列服务场景应用案例

    一.消息队列场景简介 “消息”是在两台计算机间传送的数据单位.消息可以非常简单,例如只包含文本字符串:也可以更复杂,可能包含嵌入对象.消息被发送到队列中,“消息队列”是在消息的传输过程中保存消息的容器 ...

  6. Redis作为消息队列服务场景应用案例

    NoSQL初探之人人都爱Redis:(3)使用Redis作为消息队列服务场景应用案例   一.消息队列场景简介 “消息”是在两台计算机间传送的数据单位.消息可以非常简单,例如只包含文本字符串:也可以更 ...

  7. 【转】NoSQL初探之人人都爱Redis:(3)使用Redis作为消息队列服务场景应用案例

    一.消息队列场景简介 “消息”是在两台计算机间传送的数据单位.消息可以非常简单,例如只包含文本字符串:也可以更复杂,可能包含嵌入对象.消息被发送到队列中,“消息队列”是在消息的传输过程中保存消息的容器 ...

  8. 使用Redis作为消息队列服务场景应用案例

    一.消息队列场景简介 "消息"是在两台计算机间传送的数据单位.消息可以非常简单,例如只包含文本字符串:也可以更复杂,可能包含嵌入对象.消息被发送到队列中,"消息队列&qu ...

  9. Redis系列(二):Redis的数据类型及命令操作

    原文链接(转载请注明出处):Redis系列(二):Redis的数据类型及命令操作 Redis 中常用命令 Redis 官方的文档是英文版的,当然网上也有大量的中文翻译版,例如:Redis 命令参考.这 ...

随机推荐

  1. 算法(Algorithms)第4版 练习 1.5.4

    代码实现: package com.qiusongde; import edu.princeton.cs.algs4.StdIn; import edu.princeton.cs.algs4.StdO ...

  2. 使用JobControl控制MapReduce任务

    代码结构 BeanWritable:往数据库读写使用的bean ControlJobTest:JobControl任务控制 DBInputFormatApp:将关系型数据库的数据导入HDFS,其中包含 ...

  3. tomcat7 中的坑。 关于welcome-list和servlet-mapping

    web.xml中, 使用default servlet设置了针对静态资源后缀名的过滤. 并且设置了welcome-list, 使用jetty和tomcat6启动一切正常, 但是使用tomcat7则出现 ...

  4. Xcode 中代码提示不显示

    解决办法: Xcode->Window->Organizer->Projects选中你的项目,点击如下图Derived Data右侧的Delete按钮 DerivedData从字面上 ...

  5. php设计模式课程---7、装饰器模式如何使用

    php设计模式课程---7.装饰器模式如何使用 一.总结 一句话总结: 装饰器的核心是获取了文章类整个类,而不是获取了文章内容,有了这个文章类,我想给你加多少装饰就给你加多少装饰(将文章这个类封装进去 ...

  6. 模拟Windows任务管理器CPU使用率的动态折线图-农夫山泉

    Delphi的TCanvas类可以实现各种复杂的图形输出功能,基于近期项目的需求,利用它实现了一个很炫的动态折线图(模拟了资源管理器中CPU使用率的折线图),可以直观地展现出数值的实时变化情况. 这段 ...

  7. IIS部署网站(建站)

      双击IIS图标,运行IIS服务器: 如下图所示: 2 IIS打开后,选中网站,然后右键,在弹出的菜单中,选择新建,然后选择网站:如下图所hi: 3 在弹出的窗口中,直接点击下一步:如下图所示: 4 ...

  8. 几个常用的url生成二维码的接口

    找到了几个URL生成的接口,速度上可能会有差别,可试验后选用,我用过第一个,分享: <!doctype html> <html lang="en"> < ...

  9. Java丨jsoup网络爬虫模拟登录思路解析

    直奔主题: 本篇文章是给有jsoup抓包基础的人看的...... 今天小编给大家写一篇对于jsoup抓包时需要输入验证码的解决方法之一.大神就绕道,嘿嘿~ 任何抓包的基础都是基于Http协议来进行这个 ...

  10. hdu1556Color the ball线段树区间更新

    题目链接 线段树区间更新更新一段区间,在更新区间的过程中,区间被分成几段,每一段的左右界限刚好是一个节点的tree[node].left和tree[node].right(如果不是继续分,直到是为止) ...