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

来源:互联网 发布:淘宝开店时长 编辑:程序博客网 时间:2024/05/22 05:24

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

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


    可以看到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方法。演示如下


    单行为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。演示如下:


    这个指令是可以跟多个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操作。演示如下:


    上图中的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<String, String> map;    @Before    public void setUp() {        client = MyJedisFactory.defaultJedis();        map = new HashMap<String, String>(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<String> 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<String> redisKeys = client.hkeys(KEY);        Set<String> 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<String, String> redisMap = client.hgetAll(KEY);        assertNotNull(redisMap);        assertEquals(redisMap.size(), map.size());        Set<Map.Entry<String, String>> entrySet = map.entrySet();        for(Map.Entry<String, String> 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入门指南》

    


原创粉丝点击