Redis从基础命令到实战之散列类型(Hash)
从上一篇的实例中可以看出,用字符串类型存储对象有一些不足,在存储/读取时需要进行序列化/反序列化,即时只想修改一项内容,如价格,也必须修改整个键值。不仅增大开发的复杂度,也增加了不必要的性能开销。
一个更好的选择是使用散列类型,或称为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)相关推荐
- Mysql常用基础命令操作实战
目录 一 启动与关闭MySQL 3 1.1 单实例MySQL启动与关闭方法 3 ※1※ 常规启动关闭数据库方式(推荐) 3 1.2 多实例MySQL启动与关闭 ...
- Redis数据类型:散列类型
2019独角兽企业重金招聘Python工程师标准>>> 概要 散列类型存放字段(filed)与字段值(value),字段值只能存放字符串,不能嵌套存放其他数据类型 散列类型适合存放对 ...
- redis 自减命令_Redis 实战 —— 04. Redis 数据结构常用命令简介
字符串 P39 Redis 的字符串是一个有字节组成的序列,可以存储以下 3 种类型的值:字节串(byte string).整数.浮点数. 在需要的时候, Redis 会将整数转换成浮点数.整数的取值 ...
- 数据库-初识Redis(基础命令)
Redis基础命令 redis基础 基本数据类型 1.String 2.list 3.hash 4.set 5.zset 其他数据类型 HyperLogLog bitmap 位图 Geospatial ...
- redis的基础命令操作
文章目录 前言 一.字符串类型 二.哈希类型 三.列表类型 四.集合类型 五.有序集合类型 六.通过命令 前言 redis的数据结构 redis存储的是key,value格式的数据,其中的key是字符 ...
- 野生前端的数据结构基础练习(5)——散列
网上的相关教程非常多,基础知识自行搜索即可. 习题主要选自Orelly出版的<数据结构与算法javascript描述>一书. 参考代码可见:https://github.com/dashn ...
- linux基础命令大全(一)——文件类型和常用目录
前言 Linux不像windows那样一切操作都有图形界面.Linux中一切皆为文件,在Linux中做的操作就是对文件的操作,这就需要使用命令来对文件进行操作.但是在学习基础命令之前我认为理清linu ...
- 数据结构笔记(六)——散列(Hash Table)之双散列和再散列(4)
虽然平方探测排除了一次聚集,但散列到同一位置的元素仍然会探测相同的备选位置,比如当冲突函数为i^2时,对于每个要插入的X,其向前探测地步长都是0,1,4,9,16,这样对于散列到同一位置的X,他们都会 ...
- Linux基础命令学习——实战篇(给swap分区增加500M)
给swap分区增加500M Swap分区,即交换区 Swap空间的作用可简单描述为:当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用,那些被释放的空间可能来 ...
最新文章
- Codeforces.1051F.The Shortest Statement(最短路Dijkstra)
- CSS实现网页图片预加载
- oc45--多对象内存管理 优化
- html5图片加载不了,webView加载html图片遇到的问题解决
- 开发者必备的15 个Web开发工具
- c语言程序设计橙皮,橙皮_中药词典C_中医中药网
- java中类与对象回顾总结
- android与html注册登录,Android登录注册源码
- ITK:警告定向到文件
- QSplitter分割器窗口比例设定
- Hibernate的多表查询,分装到一个新的实体类中的一个方法
- Eclipse调试进入JDK源码
- linux技能点七 shell
- pycharm + python36 + opencv + opencv_contrib库的安装
- RayMarching1:用射线的方式画一个球
- 使用百度echarts仿雪球分时图(二)
- ABB变频器通过labview和上位机modbus通讯
- idea安装插件plugin(主要针对网络连接不上的情况)
- 华为p8 root android6,华为P6一键ROOT权限获取及USB驱动
- 机器人运动规划技术介绍
热门文章
- 响应式网页设计简单入门
- 通过显式寻找物体的 extremity 区域加快 DETR 的收敛:Conditional DETR
- 哥大首位华裔女校长:人工智能的春天来了
- CVPR 2020 | 北大Futurewei提出 GraphTER:无监督图变换共变表征学习
- 算法岗百里挑一热爆了,全球AI大厂薪酬大起底
- 2018年12月精选文章目录一览
- 机器学习、深度学习方面书籍收集(持续更新……)
- 设置单元格填充方式_【WPS神技能】Excel表格中单元格内的双色填充效果有点意思!...
- 在TensorFlow中使用pipeline加载数据
- 学习l1图做图像分析