了解Redis数据结构——链表(linked-list)

链表结构是Redis中的一个常用的结构,他可以存储多个字符串,而且它是有序的,能够存储40多亿个节点。Redis链表是双向的,因此既可以从左到右,也可以从右到左遍历它存储的节点,链表结构如下:
在这里插入图片描述
由于它是双向链表,所以它的读性能就会相对丧失,而插入和删除就会显得很便利。它的增删操作与双向链表同。

因为是双向链表结构,所以Redis链表命令分为左操作和右操作两种命令,左操作意味着是从左到右,右操作意味着是从右到左。Redis关于链表的命令如下:

  • lpush key node1 [node2…]:把节点node1加入到链表的最左端,如果是node1,node2…noden这样的加入,那么链表的开头从左到右的顺序是noden…,node2,node1。
  • rpush key node1 [node2…]:把节点node1加入到链表的最右端,如果是node1,node2…noden这样的加入,那么链表的开头从左到右的顺序是noden1,node2…noden。
  • lindex key index:从左往右读取下标为index的节点,返回节点字符串,从0开始算。
  • llen key:求链表的长度。返回链表节点数。
  • lpop key:删除左边第一个节点,并将其返回。
  • rpop key:删除右边第一个节点,并将其返回。
  • linsert key before|after pivot node:插入一个节点node,并且可以指定在值为pivot node的结点的前面或者后面。如果list不存在,则报错;如果没有值为对应pivot node,也会插入失败。
  • lpushx list node:如果存在key为list的链表,则插入节点node,并且作为从左到右的第一个节点,如果list不存在,则失败。
  • rpushx list node:如果存在key为list的链表,则插入节点node,并且作为从右到左的第一个节点,如果list不存在,则失败。
  • lrange list start end:获取链表从start下标到end下标的节点数,包含start和end下标的值。
  • lrem list count value:如果count为0,则删除所有值为value的节点;如果count不为0,则先对count取绝对值,假设极为abs,然后从左到右删除不大于abs个等于value的节点。
  • lset key index node:设置下标为index的节点的值为node。
  • ltrim key start stop:修建链表,只保留从start到stop的区间的节点,其余的都删除掉,包含start和end的下标的节点会保留。

但是上述的所有方法都是进程不安全的,因为当我们操作这些命令的时候,其他的Redis的客户端也可能操作同一个链表,这样就会造成并发数据安全和一致性的问题。为了解决这些问题,Redis提供了链表的阻塞命令,它们在运行的时候,会给链表加锁,以保证操作链表的命令安全性。链表的阻塞命令如下:

  • blpop key timeout:移出并获取列表的第一个元素,如果链表没有元素会阻塞链表直到等待超时或发现可弹出元素为止。相对于lpop命令,它是安全的。
  • brpop key timeout:移出并获取列表的最后一个元素,如果链表没有元素会阻塞链表直到等待超时或发现可弹出元素为止。相对于rpop命令,它是安全的。
  • rpoplpush key src dest:按从左到右的顺序,将一个链表的最后一个元素移除,并插入到目标链表的最左边。不能设置超时时间。
  • brpoplpush key src dest timeout:按从左到右的顺序,将一个链表的最后一个元素移除,并插入到目标链表的最左边。并可以设置超时时间。

我们在用一个简单的demo来实现这些功能,代码如下:

redisSpring-cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="50" />
        <property name="maxTotal" value="100" />
        <property name="maxWaitMillis" value="20000" />
    </bean>

    <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />

    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="localhost" />
        <property name="port" value="6379" />
        <property name="password" value="123456" />
        <property name="poolConfig" ref="poolConfig" />
    </bean>

    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="defaultSerializer" ref="stringRedisSerializer" />
        <property name="keySerializer" ref="stringRedisSerializer" />
        <property name="valueSerializer" ref="stringRedisSerializer" />
    </bean>
</beans>

testList.java

package com.ssm.redis1.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.connection.RedisListCommands;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import static com.sun.xml.internal.messaging.saaj.packaging.mime.util.ASCIIUtility.getBytes;

public class testList {
    public static void main(String[] args){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("redisSpring-cfg.xml");
        RedisTemplate redisTemplate=applicationContext.getBean(RedisTemplate.class);
        try {
            //删除链表,以便我们反复测试
            redisTemplate.delete("list");
            //把node3插入链表list
            redisTemplate.opsForList().leftPush("list","node3");
            List<String> nodeList=new ArrayList<String>();
            for(int i=2;i>=1;i--){
                nodeList.add("node"+1);
            }
            //相当于lpush把多个节点从左插入链表
            redisTemplate.opsForList().leftPushAll("list",nodeList);
            //从右边插入一个节点
            redisTemplate.opsForList().rightPush("list","node4");
            //获取下标为0的节点
            String node1=(String)redisTemplate.opsForList().index("list",0);
            //获取链表的长度
            long length=redisTemplate.opsForList().size("list");
            //从左边弹出一个节点
            String lpop=(String)redisTemplate.opsForList().leftPop("list");
            //从右边弹出一个节点
            String rpop=(String)redisTemplate.opsForList().rightPop("list");
            //注意,需要使用更为底层的命令才能操作linsert命令
            //使用linsert命令在node2前插入一个节点
            redisTemplate.getConnectionFactory().getConnection().lInsert(getBytes("utf-8"),
                                                                         RedisListCommands.Position.BEFORE,
                                                                         "node2".getBytes("utf-8"),
                                                                         "before_node".getBytes("utf-8"));

            //使用linsert命令在node2后插入一个节点
            redisTemplate.getConnectionFactory().getConnection().lInsert(getBytes("utf-8"),
                                                                         RedisListCommands.Position.BEFORE,
                                                                         "node2".getBytes("utf-8"),
                                                                         "after_node".getBytes("utf-8"));

            //判断list是否存在,如果存在则从左边插入head节点
            redisTemplate.opsForList().leftPushIfPresent("list","head");
            //判断list是否存在,如果存在则从右边边插入end节点
            redisTemplate.opsForList().rightPushIfPresent("list","end");
            //从左到右,或者下摆哦为0搭配10的节点元素
            List valueList=redisTemplate.opsForList().range("list",0,10);
            nodeList.clear();
            for(int i=1;i<3;i++){
                nodeList.add("node");
            }
            //在链表左边插入三个值为node的节点
            redisTemplate.opsForList().leftPushAll("list",nodeList);
            //从左到右删除至多三个node节点
            redisTemplate.opsForList().remove("list",3,"node");
            //给链表下标为0的节点设置新值
            redisTemplate.opsForList().set("list",0,"new_head_node");
        }catch (UnsupportedEncodingException ex){
            ex.printStackTrace();
        }
        //打印链表数据
        printList(redisTemplate,"list");
    }

    private static void printList(RedisTemplate redisTemplate, String list) {
        //链表长度
        Long size=redisTemplate.opsForList().size(list);
        //获取整个链表的值
        List valueList=redisTemplate.opsForList().range(list,0,size);
        //打印
        System.out.println(valueList);
    }
}

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值