Redis从基础命令到实战之列表类型(List)
经过上一篇基于Redis散列类型的改造后,实战练习中的商品管理已经具备了增加、修改整体、修改部分属性和分页查询功能,但仍然不支持删除商品的功能。这是因为商品总数是以一个自增数字记录的,且关联了新商品key的生成,删除商品后不能直接减小总数,进而会影响到分页的计算。
在本节中将完善这个功能,使用一个新的数据类型 - 列表类型(List)来保存所有商品ID,计算商品总量时通过计算列表类型长度获得,删除商品时也同时删除列表中的ID。
列表类型用于存储可重复的字符串列表,内部按照添加的先后保持顺序,在列表两端添加和取出元素非常高效,常见的应用场景是社交网站取出最新发表内容列表,也可以用于实现消息队列。先看常用的操作。
一、常用命令
import java.util.List; import redis.clients.jedis.Jedis; import redis.clients.jedis.SortingParams; import redis.clients.jedis.BinaryClient.LIST_POSITION; public class ListExample { public static void main(String[] args) { Jedis jedis = JedisProvider.getJedis(); jedis.flushDB(); //lpush 在左边添加 String fruitKey = "fruit"; long lpush = jedis.lpush(fruitKey, "apple"); print("lpush " + fruitKey + " apple=" + lpush); lpush = jedis.lpush(fruitKey, "banana"); print("lpush " + fruitKey + " banana=" + lpush); jedis.lpush(fruitKey, "apple"); jedis.lpush(fruitKey, "apple"); jedis.lpush(fruitKey, "orange"); jedis.lpush(fruitKey, "grape"); jedis.lpush(fruitKey, "peach"); jedis.lpush(fruitKey, "lemon"); //rpush 在右边添加 String numberKey = "numbers"; jedis.rpush(numberKey, "5"); jedis.rpush(numberKey, "4"); jedis.rpush(numberKey, "2"); jedis.rpush(numberKey, "1"); //lrange 获得列表片段 print("lrange " + fruitKey + " [0,-1]=" + jedis.lrange(fruitKey, 0, -1)); print("lrange " + numberKey + " [0,-1]=" + jedis.lrange(numberKey, 0, -1)); print("lrange " + fruitKey + " [1,3]=" + jedis.lrange(fruitKey, 1, 3)); //删除列表指定的值,第二个参数为删除的个数(有重复时),后添加进去的值先被删,类似于出栈,返回删除个数 Long del = jedis.lrem(fruitKey, 2, "apple"); print("lrem " + fruitKey + " apple*2=" + del); print("after lrem, lrange " + fruitKey + " [0,-1]=" + jedis.lrange(fruitKey, 0, -1)); //删除区间以外的数据, 成功返回 OK String ltrim = jedis.ltrim(fruitKey, 0, 3); print("ltrim " + fruitKey + " [0,3]=" + ltrim); print("after ltrim, lrange " + fruitKey + " [0,-1]=" + jedis.lrange(fruitKey, 0, -1)); //列表元素出栈 String lpop = jedis.lpop(fruitKey); print("lpop " + fruitKey + " =" + lpop); print("after lpop, lrange " + fruitKey + " [0,-1]=" + jedis.lrange(fruitKey, 0, -1)); //修改列表中指定下标的值 String lset = jedis.lset(fruitKey, 0, "mango"); print("lset " + fruitKey + " 0=" + lset); print("after lset, lrange " + fruitKey + " [0,-1]=" + jedis.lrange(fruitKey, 0, -1)); //数组长度 print("llen " + fruitKey + " =" + jedis.llen(fruitKey)); //排序 SortingParams sortingParameters = new SortingParams(); sortingParameters.alpha(); sortingParameters.limit(0, 3); List<String> list = jedis.sort(fruitKey, sortingParameters); String sort = ""; for(int i = 0; i < list.size(); i++) { sort += list.get(i) + " "; } print("sort " + fruitKey + " =" + sort); //获取列表指定下标的值, 不存在返回null int pos = 2; String lindex = jedis.lindex(fruitKey, pos); print("lindex " + fruitKey + " 2=" + lindex); //在指定元素前/后插入 jedis.linsert(numberKey, LIST_POSITION.BEFORE, "2", "3"); print("after linsert, lrange " + numberKey + " [0,-1]=" + jedis.lrange(numberKey, 0, -1)); String value = jedis.rpoplpush(numberKey, "dst"); while(value != null) { System.out.println("rpoplpush " + numberKey + " =" + value); value = jedis.rpoplpush(numberKey, "dst"); } print("after rpoplpush, lrange dst [0,-1]=" + jedis.lrange("dst", 0, -1)); jedis.close(); } private static void print(String info) { System.out.println(info); System.out.println("------------------------------------------------------"); System.out.println(); } }二、实践练习
在上一篇商品管理示例的基础上,增加删除商品的功能。
修改添加商品的功能代码,增加了列表类型保存所有新加入的商品ID
/** * 添加一个商品 * @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; boolean added = jedis.hmset(key, map).equals("OK"); if(added) { jedis.rpush("goods:list", String.valueOf(id)); } return added; }添加删除商品功能代码
/** * 删除商品 * @param id * @return */ public boolean delGoods(long id) { long deleted = jedis.del("goods:" + id); if(deleted == 1) { jedis.lrem("goods:list", 1, String.valueOf(id)); } return deleted == 1; }还需要修改获得商品总数和分页查询商品的代码,商品总数和ID列表改为从goods:list中获得
/** * 读取商品总数 * @return */ public long getTotalCount() { String key = "goods:list"; return jedis.llen(key); }
/** * 读取用于分页的商品列表 * @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 - 1; if(to > totals) { to = totals; } List<Goods> goodsList = new ArrayList<>(); List<String> idList = jedis.lrange("goods:list", from, to); for(String id : idList) { String key = "goods:" + id; 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) { ListLession ll = new ListLession(); ll.clear(); //添加一批商品 for(int i = 0; i< 42; i++) { Goods goods = new Goods(0, "goods" + String.format("%05d", (i + 1)), i); ll.addGoods(goods); } //读取商品总数 System.out.println("商品总数: " + ll.getTotalCount()); //删除第29件商品 ll.delGoods(29); System.out.println("删除后商品总数: " + ll.getTotalCount()); //分页显示 List<Goods> list = ll.getGoodsList(2, 20); System.out.println("第二页商品:"); for(Goods goods : list) { System.out.println(goods); } }
现在,商品管理已经具备了增删改查功能,但是Redis还有两个数据类型没有被使用到,下一节将使用集合类型来实现为商品添加分类的功能。