zoukankan      html  css  js  c++  java
  • Redis从基础命令到实战之散列类型(Hash)

    从上一篇的实例中可以看出,用字符串类型存储对象有一些不足,在存储/读取时需要进行序列化/反序列化,即时只想修改一项内容,如价格,也必须修改整个键值。不仅增大开发的复杂度,也增加了不必要的性能开销。

    一个更好的选择是使用散列类型,或称为Hash表。散列类型与Java中的HashMap相似,是一组键值对的集合,且支持单独对其中一个键进行增删改查操作。使用散列类型存储前面示例中的商品对象,结构如下图所示:

    下面先通过示例代码来看散列类型常用的操作命令

    一、常用命令

    HashExample.java

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. import java.util.Arrays;  
    2. import java.util.HashMap;  
    3. import java.util.List;  
    4. import java.util.Map;  
    5. import java.util.Set;  
    6.   
    7. import redis.clients.jedis.Jedis;  
    8.   
    9. public class HashExample {  
    10.   
    11.     public static void main(String[] args) {  
    12.         Jedis jedis = JedisProvider.getJedis();  
    13.         jedis.flushDB();  
    14.   
    15.         // 为了避免混淆,下文中对Hash表中的键统称为field  
    16.   
    17.         String key = "goods";  
    18.   
    19.         // hset 仅当操作在hash中创建新field时返回1  
    20.         Long hset = jedis.hset(key, "id", "1");  
    21.         print("hset id 1=" + hset + "; value=" + jedis.hget(key, "id"));  
    22.   
    23.         // 如果field已存在则执行修改,并返回0  
    24.         hset = jedis.hset(key, "id", "2");  
    25.         print("hset id 2=" + hset + "; value=" + jedis.hget(key, "id"));  
    26.   
    27.         // hexists 判断field是否存在  
    28.         boolean hexists = jedis.hexists(key, "id");  
    29.         print("hexists id=" + hexists);  
    30.         hexists = jedis.hexists(key, "title");  
    31.         print("hexists title=" + hexists);  
    32.   
    33.         // hsetex 如果field不存在则添加, 已存在则不会修改值, 可用来添加要求不重复的field  
    34.         Long hsetnx = jedis.hsetnx(key, "id", "3");  
    35.         print("hsetnx id 3=" + hsetnx + "; value=" + jedis.hget(key, "id"));  
    36.         hsetnx = jedis.hsetnx(key, "title", "商品001");  
    37.         print("hsetnx title 商品001=" + hsetnx + "; value=" + jedis.hget(key, "title"));  
    38.   
    39.         // hmset 设置多个field  
    40.         Map<String, String> msets = new HashMap<>();  
    41.         msets.put("color", "red");  
    42.         msets.put("width", "100");  
    43.         msets.put("height", "80");  
    44.         String hmset = jedis.hmset(key, msets);  
    45.         print("hmset color,width,height=" + hmset);  
    46.   
    47.         // hincr 新增整数类型的键值对或增加值  
    48.         long hincr = jedis.hincrBy(key, "price", 4l);  
    49.         print("hincrBy price 4=" + hincr + "; value=" + jedis.hget(key, "price"));  
    50.   
    51.         // hlen 读取field数量  
    52.         print("hlen=" + jedis.hlen(key));  
    53.   
    54.         // hkeys 读取所有field  
    55.         Set<String> sets = jedis.hkeys(key);  
    56.         print("hkeys=" + Arrays.toString(sets.toArray()));  
    57.   
    58.         // hvals 读取所有值  
    59.         List<String> list = jedis.hvals(key);  
    60.         print("hvals=" + Arrays.toString(list.toArray()));  
    61.   
    62.         // hgetAll 读取所有键值对  
    63.         System.out.println("hgetAll 读取所有键值对");  
    64.         Map<String, String> maps = jedis.hgetAll(key);  
    65.         for (String field : maps.keySet()) {  
    66.             System.out.println("hget " + field + "=" + maps.get(field));  
    67.         }  
    68.         System.out.println("------------------------------------------------------");  
    69.         System.out.println();  
    70.   
    71.         // hdel 删除field  
    72.         Long hdel = jedis.hdel(key, "id");  
    73.         print("hdel id=" + hdel);  
    74.   
    75.         // 删除多个field  
    76.         hdel = jedis.hdel(key, "color", "width", "height");  
    77.         print("hdel color,width,height=" + hdel);  
    78.   
    79.         // hincrBy 在整数类型值上增加, 返回修改后的值  
    80.         Long hincrBy = jedis.hincrBy(key, "price", 100l);  
    81.         print("hincrBy price 100=" + hincrBy);  
    82.   
    83.         // hget 读取单个field的值  
    84.         String hget = jedis.hget(key, "title");  
    85.         print("hget title=" + hget);  
    86.   
    87.         // hmget 批量读取field的值  
    88.         jedis.hmget(key, "title", "price");  
    89.         list = jedis.hvals(key);  
    90.         print("hmget title,price=" + Arrays.toString(list.toArray()));  
    91.   
    92.         jedis.close();  
    93.     }  
    94.   
    95.     private static void print(String info) {  
    96.         System.out.println(info);  
    97.         System.out.println("------------------------------------------------------");  
    98.         System.out.println();  
    99.     }  
    100.   
    101. }  

    二、实践练习

    对前一篇基于字符串类型的商品管理示例改造,以散列类型存储商品,并增加单独修改标题和修改价格的接口。

    首先是添加商品代码

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. /** 
    2.  * 添加一个商品 
    3.  * @param goods 
    4.  * @return 
    5.  */  
    6. public boolean addGoods(Goods goods) {  
    7.     long id = getIncrementId();  
    8.     Map<String, String> map = new HashMap<>();  
    9.     map.put("id", String.valueOf(id));  
    10.     map.put("title", goods.getTitle());  
    11.     map.put("price", String.valueOf(goods.getPrice()));  
    12.     String key = "goods:" + id;  
    13.     return jedis.hmset(key, map).equals("OK");  
    14. }  

    然后增加两个单独修改属性的方法

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. /** 
    2.  * 修改商品标题 
    3.  * @param goods 
    4.  * @return 
    5.  */  
    6. public boolean updateTitle(long id, String title) {  
    7.     String key = "goods:" + id;  
    8.     return jedis.hset(key, "title", title) == 0;  
    9. }  
    10.   
    11. /** 
    12.  * 修改商品价格 
    13.  * @param id 
    14.  * @param price 
    15.  * @return 
    16.  */  
    17. public boolean updatePrice(long id, float price) {  
    18.     String key = "goods:" + id;  
    19.     return jedis.hset(key, "price", String.valueOf(price)) == 0;  
    20. }  

    最后还需要修改读取商品列表的方法

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. /** 
    2.  * 读取用于分页的商品列表 
    3.  * @param pageIndex 页数 
    4.  * @param pageSize 每页显示行数 
    5.  * @return 
    6.  */  
    7. public List<Goods> getGoodsList(int pageIndex, int pageSize) {  
    8.     int totals = (int)getTotalCount();  
    9.     int from = (pageIndex - 1) * pageSize;  
    10.     if(from < 0) {  
    11.         from = 0;  
    12.     }  
    13.     else if(from > totals) {  
    14.         from = (totals / pageSize) * pageSize;  
    15.     }  
    16.     int to = from + pageSize;  
    17.     if(to > totals) {  
    18.         to = totals;  
    19.     }  
    20.     List<Goods> goodsList = new ArrayList<>();  
    21.     for(int i = from; i < to; i++) {  
    22.         String key = "goods:" + (i + 1);  
    23.         Map<String, String> maps = jedis.hgetAll(key);  
    24.         Goods goods = new Goods();  
    25.         goods.setId(NumberUtils.toLong(maps.get("id")));  
    26.         goods.setTitle(maps.get("title"));  
    27.         goods.setPrice(NumberUtils.toFloat(maps.get("price")));  
    28.         goodsList.add(goods);  
    29.     }  
    30.     return goodsList;  
    31. }  

    测试代码

    [java] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. public static void main(String[] args) {  
    2.     HashLession hl = new HashLession();  
    3.     hl.clear();  
    4.       
    5.     //添加一批商品  
    6.     for(int i = 0; i< 41; i++) {  
    7.         Goods goods = new Goods(0, "goods" + String.format("%05d", i), i);  
    8.         hl.addGoods(goods);  
    9.     }  
    10.     //读取商品总数  
    11.     System.out.println("商品总数: " + hl.getTotalCount());  
    12.     //修改商品价格  
    13.     for(int i = 1; i <= hl.getTotalCount(); i++) {  
    14.         hl.updatePrice(i, new Random().nextFloat());  
    15.     }  
    16.     //分页显示  
    17.     List<Goods> list = hl.getGoodsList(2, 20);  
    18.     System.out.println("第二页商品:");  
    19.     for(Goods goods : list) {  
    20.         System.out.println(goods);  
    21.     }  
    22. }  

    到目前为止,此示例仍然不能支持删除商品的功能,这是因为商品总数是以一个自增数字记录的,且关联了新商品key的生成,删除商品后不能直接减小总数,进而影响到分页的计算。一个比较低效的办法遍历数据库并累加符合规则的key总数,但是更好的做法是以链表保存所有存活的id,这将在下一篇介绍。

    源码下载

  • 相关阅读:
    二分搜索树的深度优先遍历和广度优先遍历
    数据结构与算法之非比较排序【Java】
    数据结构与算法之比较排序【Java】
    像素 转换 px dp
    Toast
    MySQL丶auto_increment
    MariaDB · 性能优化 · Extended Keys
    加唯一索引怎么会导致丢数据
    语句执行错误一· Count(Distinct) ERROR
    innodb参数 &#183; innodb_flush_log_at_trx_commit
  • 原文地址:https://www.cnblogs.com/sa-dan/p/6836884.html
Copyright © 2011-2022 走看看