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

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

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

一、常用命令

HashExample.java

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;import redis.clients.jedis.Jedis;public class HashExample {public static void main(String[] args) {Jedis jedis = JedisProvider.getJedis();jedis.flushDB();// 为了避免混淆,下文中对Hash表中的键统称为fieldString key = "goods";// hset 仅当操作在hash中创建新field时返回1Long hset = jedis.hset(key, "id", "1");print("hset id 1=" + hset + "; value=" + jedis.hget(key, "id"));// 如果field已存在则执行修改,并返回0hset = jedis.hset(key, "id", "2");print("hset id 2=" + hset + "; value=" + jedis.hget(key, "id"));// hexists 判断field是否存在boolean hexists = jedis.hexists(key, "id");print("hexists id=" + hexists);hexists = jedis.hexists(key, "title");print("hexists title=" + hexists);// hsetex 如果field不存在则添加, 已存在则不会修改值, 可用来添加要求不重复的fieldLong hsetnx = jedis.hsetnx(key, "id", "3");print("hsetnx id 3=" + hsetnx + "; value=" + jedis.hget(key, "id"));hsetnx = jedis.hsetnx(key, "title", "商品001");print("hsetnx title 商品001=" + hsetnx + "; value=" + jedis.hget(key, "title"));// hmset 设置多个fieldMap<String, String> msets = new HashMap<>();msets.put("color", "red");msets.put("width", "100");msets.put("height", "80");String hmset = jedis.hmset(key, msets);print("hmset color,width,height=" + hmset);// hincr 新增整数类型的键值对或增加值long hincr = jedis.hincrBy(key, "price", 4l);print("hincrBy price 4=" + hincr + "; value=" + jedis.hget(key, "price"));// hlen 读取field数量print("hlen=" + jedis.hlen(key));// hkeys 读取所有fieldSet<String> sets = jedis.hkeys(key);print("hkeys=" + Arrays.toString(sets.toArray()));// hvals 读取所有值List<String> list = jedis.hvals(key);print("hvals=" + Arrays.toString(list.toArray()));// hgetAll 读取所有键值对System.out.println("hgetAll 读取所有键值对");Map<String, String> maps = jedis.hgetAll(key);for (String field : maps.keySet()) {System.out.println("hget " + field + "=" + maps.get(field));}System.out.println("------------------------------------------------------");System.out.println();// hdel 删除fieldLong hdel = jedis.hdel(key, "id");print("hdel id=" + hdel);// 删除多个fieldhdel = jedis.hdel(key, "color", "width", "height");print("hdel color,width,height=" + hdel);// hincrBy 在整数类型值上增加, 返回修改后的值Long hincrBy = jedis.hincrBy(key, "price", 100l);print("hincrBy price 100=" + hincrBy);// hget 读取单个field的值String hget = jedis.hget(key, "title");print("hget title=" + hget);// hmget 批量读取field的值jedis.hmget(key, "title", "price");list = jedis.hvals(key);print("hmget title,price=" + Arrays.toString(list.toArray()));jedis.close();}private static void print(String info) {System.out.println(info);System.out.println("------------------------------------------------------");System.out.println();}}

二、实践练习

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

首先是添加商品代码

    /*** 添加一个商品* @param goods* @return*/public boolean addGoods(Goods goods) {long id = getIncrementId();Map<String, String> map = new HashMap<>();map.put("id", String.valueOf(id));map.put("title", goods.getTitle());map.put("price", String.valueOf(goods.getPrice()));String key = "goods:" + id;return jedis.hmset(key, map).equals("OK");}

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

   /*** 修改商品标题* @param goods* @return*/public boolean updateTitle(long id, String title) {String key = "goods:" + id;return jedis.hset(key, "title", title) == 0;}/*** 修改商品价格* @param id* @param price* @return*/public boolean updatePrice(long id, float price) {String key = "goods:" + id;return jedis.hset(key, "price", String.valueOf(price)) == 0;}

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

  /*** 读取用于分页的商品列表* @param pageIndex 页数* @param pageSize 每页显示行数* @return*/public List<Goods> getGoodsList(int pageIndex, int pageSize) {int totals = (int)getTotalCount();int from = (pageIndex - 1) * pageSize;if(from < 0) {from = 0;}else if(from > totals) {from = (totals / pageSize) * pageSize;}int to = from + pageSize;if(to > totals) {to = totals;}List<Goods> goodsList = new ArrayList<>();for(int i = from; i < to; i++) {String key = "goods:" + (i + 1);Map<String, String> maps = jedis.hgetAll(key);Goods goods = new Goods();goods.setId(NumberUtils.toLong(maps.get("id")));goods.setTitle(maps.get("title"));goods.setPrice(NumberUtils.toFloat(maps.get("price")));goodsList.add(goods);}return goodsList;}

测试代码

 public static void main(String[] args) {HashLession hl = new HashLession();hl.clear();//添加一批商品for(int i = 0; i< 41; i++) {Goods goods = new Goods(0, "goods" + String.format("%05d", i), i);hl.addGoods(goods);}//读取商品总数System.out.println("商品总数: " + hl.getTotalCount());//修改商品价格for(int i = 1; i <= hl.getTotalCount(); i++) {hl.updatePrice(i, new Random().nextFloat());}//分页显示List<Goods> list = hl.getGoodsList(2, 20);System.out.println("第二页商品:");for(Goods goods : list) {System.out.println(goods);}}

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

源码下载

转载于:https://www.cnblogs.com/autfish/p/5612045.html

Redis从基础命令到实战之散列类型(Hash)相关推荐

  1. Mysql常用基础命令操作实战

    目录 一    启动与关闭MySQL    3 1.1    单实例MySQL启动与关闭方法    3 ※1※    常规启动关闭数据库方式(推荐)    3 1.2    多实例MySQL启动与关闭 ...

  2. Redis数据类型:散列类型

    2019独角兽企业重金招聘Python工程师标准>>> 概要 散列类型存放字段(filed)与字段值(value),字段值只能存放字符串,不能嵌套存放其他数据类型 散列类型适合存放对 ...

  3. redis 自减命令_Redis 实战 —— 04. Redis 数据结构常用命令简介

    字符串 P39 Redis 的字符串是一个有字节组成的序列,可以存储以下 3 种类型的值:字节串(byte string).整数.浮点数. 在需要的时候, Redis 会将整数转换成浮点数.整数的取值 ...

  4. 数据库-初识Redis(基础命令)

    Redis基础命令 redis基础 基本数据类型 1.String 2.list 3.hash 4.set 5.zset 其他数据类型 HyperLogLog bitmap 位图 Geospatial ...

  5. redis的基础命令操作

    文章目录 前言 一.字符串类型 二.哈希类型 三.列表类型 四.集合类型 五.有序集合类型 六.通过命令 前言 redis的数据结构 redis存储的是key,value格式的数据,其中的key是字符 ...

  6. 野生前端的数据结构基础练习(5)——散列

    网上的相关教程非常多,基础知识自行搜索即可. 习题主要选自Orelly出版的<数据结构与算法javascript描述>一书. 参考代码可见:https://github.com/dashn ...

  7. linux基础命令大全(一)——文件类型和常用目录

    前言 Linux不像windows那样一切操作都有图形界面.Linux中一切皆为文件,在Linux中做的操作就是对文件的操作,这就需要使用命令来对文件进行操作.但是在学习基础命令之前我认为理清linu ...

  8. 数据结构笔记(六)——散列(Hash Table)之双散列和再散列(4)

    虽然平方探测排除了一次聚集,但散列到同一位置的元素仍然会探测相同的备选位置,比如当冲突函数为i^2时,对于每个要插入的X,其向前探测地步长都是0,1,4,9,16,这样对于散列到同一位置的X,他们都会 ...

  9. Linux基础命令学习——实战篇(给swap分区增加500M)

    给swap分区增加500M Swap分区,即交换区 Swap空间的作用可简单描述为:当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用,那些被释放的空间可能来 ...

最新文章

  1. Codeforces.1051F.The Shortest Statement(最短路Dijkstra)
  2. CSS实现网页图片预加载
  3. oc45--多对象内存管理 优化
  4. html5图片加载不了,webView加载html图片遇到的问题解决
  5. 开发者必备的15 个Web开发工具
  6. c语言程序设计橙皮,橙皮_中药词典C_中医中药网
  7. java中类与对象回顾总结
  8. android与html注册登录,Android登录注册源码
  9. ITK:警告定向到文件
  10. QSplitter分割器窗口比例设定
  11. Hibernate的多表查询,分装到一个新的实体类中的一个方法
  12. Eclipse调试进入JDK源码
  13. linux技能点七 shell
  14. pycharm + python36 + opencv + opencv_contrib库的安装
  15. RayMarching1:用射线的方式画一个球
  16. 使用百度echarts仿雪球分时图(二)
  17. ABB变频器通过labview和上位机modbus通讯
  18. idea安装插件plugin(主要针对网络连接不上的情况)
  19. 华为p8 root android6,华为P6一键ROOT权限获取及USB驱动
  20. 机器人运动规划技术介绍

热门文章

  1. 响应式网页设计简单入门
  2. 通过显式寻找物体的 extremity 区域加快 DETR 的收敛:Conditional DETR
  3. 哥大首位华裔女校长:人工智能的春天来了
  4. CVPR 2020 | 北大Futurewei提出 GraphTER:无监督图变换共变表征学习
  5. 算法岗百里挑一热爆了,全球AI大厂薪酬大起底
  6. 2018年12月精选文章目录一览
  7. 机器学习、深度学习方面书籍收集(持续更新……)
  8. 设置单元格填充方式_【WPS神技能】Excel表格中单元格内的双色填充效果有点意思!...
  9. 在TensorFlow中使用pipeline加载数据
  10. 学习l1图做图像分析