Redis学习笔记之五:散列类型

    Redis的散列类型可以看做Java中的Map结构,后文简称Map,同时Redis中操纵Map的指令均已H开头。一个散列类型可以存储2的32次方-1个字段,即内部Key-Value的对数。

    可以将Map当做Java中的HashMap,这样便于快速理解。既然将其看做Map,那Redis的这个Map肯定与Java中的Map有相似之处,如下图:

Redis学习笔记之五:散列类型_第1张图片

    可以看到Key对应的Value是一个Map,而Map内部又有Key-Value键值对。内部的Key也是不能重复的,Value的值也是会被覆盖的。

    

    1、设值/取值(put/get)

    使用HSET与HGET获取Map的值,当然,与String类型一样,Map也能一次性获取多个或者设置多个值(HMSET/HMGET)。但设置多个值时,Key-Value一定要成对出现。否则当前指令会执行失败,原Map不会有任何改变。


    可以看到取出来的值是String类型的。同时Key的值没有重复,且Value的值会被覆盖,如果获取的Key对应Map的key不存在,则会返回nil。


    还有一点值得注意的是HSET,如果是插入新的Key-Value返回的是1,如果是修改旧的Value,返回是0.


    2、获取Map内部的Key-Value对数(Map的size())。

    使用HLEN指令可以获取指定的Map的Key-Value对数。同时,这个指令与STRLEN和LLEN指令一致,会在Key不存在时返回0.



    3、获取Map的所有Key或者所有Value

    指令HKEYS获取指定Map的所有Key,与Java中Map的KeySet一致。

    指令KVALS获取指定Map的所有Value,在Java的Map中,对应values(),但有所不同。

    示例如下:



    4、获取Map的所有Key-Value。

    可以使用指令HGETALL获取Map的所有Key-Value,与之对应的方法是Java中Map的entrySet方法。演示如下

Redis学习笔记之五:散列类型_第2张图片
    单行为Key,双行为Value,获取不存在的Redis的Key,返回empty list,可以从提示信息看到,这个指令是将Map遍历然后转换成List类型呈现。


    5、判断Map中的Key是否存在(containsKey)

    指令用于HEXISTS用于判断Map中对应的Key是否存在。演示如下


    我的Redis中是没有Key1这个键存在的,可以看到使用HEXISTS返回0表示Map中没有这个键,但也有可能是这个Map根本不存在。


    6、删除Map中的Key(remove)

    使用HDEL指令删除Map中的Key,格式HDEL map field。演示如下:

Redis学习笔记之五:散列类型_第3张图片

    这个指令是可以跟多个field的,可以从图看到,一次性删除了name和color属性。

    

    7、当不存在时设置

    指令HSETNX用于在Map的Key不存在时执行HSET操作,如果存在,则不作任何操作。这个指令可以看做HSET NOT EXISTS的缩写。

    演示如下:


    可以看到当Map的Key存在时,这条指令并没有对Value其进行覆盖。在Java中与之相对应的方法是putIfAbsent。与之对应的Java代码如下:

default V putIfAbsent(K key, V value) {
    V v = get(key);
    if (v == null) {
        v = put(key, value);
    }

    return v;
}

    但有区别,上述代码是分成了两步操作 1) 判断Key是否存在 2) 不存在进行put操作。而HSETNX是一个原子操作指令。

    

    8、HINCRBY指令

    看到这个指令会想起String类型的INCR、DECR等指令。的确,这个指令与INCRBY指令操作完全一致,并且都会在对应的Key不存在时执行SET操作。演示如下:

Redis学习笔记之五:散列类型_第4张图片

    上图中的Key1并不存在,可以看到HINCRBY指令在Map以及Map的Key都不存在的情况下,会先创建Map,然后HSET操作。这个指令只对Value为字符串类型的整数有效,如下图:



Redis的Java操作

   测试代码如下,略长。

package org.yamikaze.redis.test;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.junit.Assert.*;

/**
 * 使用Java代码连接测试Redis的Map类型
 * @author yamikaze
 */
public class MapTest {

    private Jedis client;
    private static final String KEY = "mapKey";
    private String key1 = "key1";
    private String value1 = "value1";
    private String key2 = "key2";
    private String value2 = "value2";
    private Map map;

    @Before
    public void setUp() {
        client = MyJedisFactory.defaultJedis();
        map = new HashMap(4);
        map.put(key1, value1);
        map.put(key2, value2);
    }

    /**
     * 测试普通的HSET与HGET
     */
    @Test
    public void testMapPutAndGet() {
        Long result = client.hset(KEY, key1, value1);
        assertTrue(result.equals(1L));
        String value = client.hget(KEY, key1);
        assertEquals(value1, value);
    }

    /**
     * 测试HMSET与HMSET
     */
    @Test
    public void testMapMPutAndMGet() {
        String result = client.hmset(KEY, map);
        assertTrue("OK".equals(result));
        List list = client.hmget(KEY, key1, key2);
        assertNotNull(list);
        assertTrue(list.size() == 2);
    }

    /**
     * 测试HLEN
     */
    @Test
    public void testMapHLen() {
        String result = client.hmset(KEY, map);
        assertTrue("OK".equals(result));
        Long size = client.hlen(KEY);
        assertEquals(size.intValue(), map.size());
    }

    /**
     * 测试HKEYS
     */
    @Test
    public void testMapHKeys() {
        String result = client.hmset(KEY, map);
        assertTrue("OK".equals(result));
        Set redisKeys = client.hkeys(KEY);
        Set keys = map.keySet();
        assertEquals(redisKeys.size(), keys.size());
        /**
         * 两个Set的元素都应该一致.
         * 不能使用将下面的代码中Keys与RedisKeys互换位置。
         * 因为HashMap返回的Set是内部KeySet,不支持add操作。
         * 你可以认为HashMap返回的Set是一个可读副本
         */
        for(String key : keys ) {
            assertFalse(redisKeys.add(key));
        }
    }

    /**
     * 测试HEXISTS
     */
    @Test
    public void testMapHExists() {
        String result = client.hmset(KEY, map);
        assertTrue("OK".equals(result));
        assertTrue(client.hexists(KEY, key1));
        assertTrue(client.hexists(KEY, key2));
        assertFalse(client.hexists(KEY, "abc"));
    }

    /**
     * 测试HDEL
     */
    @Test
    public void testMapHDel() {
        String result = client.hmset(KEY, map);
        assertTrue("OK".equals(result));
        Long delResult = client.hdel(KEY, key1, key2);
        assertTrue(delResult.intValue() == map.size());
        assertTrue(client.hlen(KEY).intValue() == 0);
    }

    /**
     * 测试HSETNX
     */
    @Test
    public void testMapHSetNX() {
        String result = client.hmset(KEY, map);
        assertTrue("OK".equals(result));
        Long result1 = client.hsetnx(KEY, key1, "123");
        String value = client.hget(KEY, key1);
        assertEquals(value1, value);
        assertTrue(result1 == 0);
        Long result2 = client.hsetnx(KEY, "abc", value2);
        assertTrue(result2 == 1);
        String value3 = client.hget(KEY, "abc");
        assertEquals(value3, value2);
    }

    /**
     * 测试HGETALL
     */
    @Test
    public void testMapGetAll() {
        String result = client.hmset(KEY, map);
        assertTrue("OK".equals(result));
        Map redisMap = client.hgetAll(KEY);
        assertNotNull(redisMap);
        assertEquals(redisMap.size(), map.size());

        Set> entrySet = map.entrySet();
        for(Map.Entry entry : entrySet) {
            String key = entry.getKey();
            String val = entry.getValue();
            String redisValue = redisMap.get(key);
            assertTrue(redisMap.containsKey(key));
            assertEquals(val, redisValue);
        }
    }

    /**
     * 测试HINCRBY
     */
    @Test
    public void testMapHIncrBy() {
        String result = client.hmset(KEY, map);
        assertTrue("OK".equals(result));

        Long result1 = client.hincrBy(KEY, "abc", 2);
        assertEquals(result1.intValue(), 2);
    }

    /**
     * HVALS指令略
     */

    @After
    public void tearDown() {
        if(client != null) {
            client.del(KEY);
        }
    }


}

总结:

   Redis的Map类型可以将其当做是Java中的Map,但又有所不同,可以将对象序列化成字符串进行存储。既然转换为字符串,就与对象原本的类型无关了。所以与Java中的Map第一个区别是Java的Map的Value只能为一种类型,而Redis的Map的Value可以多种类型(字符串形式)。第二个区别是遍历Redis中Map的Key,Value或者Key-Value会被转换为List或Set进行操作,HKEYS是转为Set,HVAL是转为List,HGETALL是转为List。为什么HKEYS是Set,而HVAL是List,是因为Map的特性使然,这一点与Java的Map保持了一致,分别对应keySet(),values(),entrySet()方法。但这三个方法又与Redis的指令有所区别,这儿不在详细讲解区别。第三个区别Redis提供了一些Java中Map没有的操作,例如HINCRBY指令。同时Java Map也有一些操作Redis没有,例如clear(), getOrDefault().


参考资料

    《Redis入门指南》

    


你可能感兴趣的:(redis)