Java版的Redis

Redis是一个基于Key-value结构的Nosql数据库,它支持各种常见的数据结构以及非常方便的操作,
与其说它是一个数据库,不如说它是一个保存各种数据结构的服务器。今天闲来没事,用 Java集合类
实现了Redis的一些基本功能,算是温习下Java了。

1.Redis入门

Redis的Key键值为字符串,但是Value值支持许多种类型,如String字符串,List链表,Set无序集合,
SortedSet有序集合,甚至是Hash表。

各种数据结构通过不同的存取方法来区分。如Set/Get直接将值存为String,LPush/LPop/LRange将
值存到一个链表中,SAdd/ZAdd则区分了无序和有序集合。

下面我们来看下在Java中使用基本的集合类如何实现这些简单而方便的操作。


2.Java版的Redis

代码的组织结构如下图:

Java版的Redis_第1张图片

[java]  view plain  copy
  1. package com.cdai.studio.redis;  
  2.   
  3. import java.util.HashSet;  
  4. import java.util.LinkedList;  
  5. import java.util.TreeSet;  
  6.   
  7. @SuppressWarnings("unchecked")  
  8. public class RedisDB {  
  9.   
  10.     private Persistence persistence = new Persistence();  
  11.       
  12.     private Serializer serializer = new Serializer();  
  13.       
  14.     private static final Object[] NULL = new Object[0];  
  15.       
  16.       
  17.     // =================================================  
  18.     //                  String value  
  19.     // =================================================  
  20.       
  21.     public void Set(String key, Object value) {  
  22.         persistence.put(key, serializer.marshal(value));  
  23.     }  
  24.       
  25.     public Object Get(String key) {  
  26.         return serializer.unmarshal(persistence.get(key));  
  27.     }  
  28.       
  29.     public Object[] MGet(String... keys) {  
  30.         Object[] values = new Object[keys.length];  
  31.         for (int i = 0; i < keys.length; i++)  
  32.             values[i] = Get(keys[i]);  
  33.         return values;  
  34.     }  
  35.       
  36.     public int Incr(String key) {  
  37.         Object value = Get(key);  
  38.         Integer valueRef = (value == null) ? 1 : (Integer) value;  
  39.         Set(key, valueRef + 1);  
  40.         return valueRef;  
  41.     }  
  42.       
  43.       
  44.     // =================================================  
  45.     //                  List value  
  46.     // =================================================  
  47.   
  48.     public void LPush(String key, Object... values) {  
  49.         Object list = persistence.get(key);  
  50.         if (list == null)  
  51.             list = new LinkedList<Object>();  
  52.         else  
  53.             list = serializer.unmarshal(list);  
  54.           
  55.         LinkedList<Object> listRef = (LinkedList<Object>) list;  
  56.         for (Object value : values)  
  57.             listRef.addFirst(value);  
  58.         persistence.put(key, serializer.marshal(list));  
  59.     }  
  60.       
  61.     public void RPush(String key, Object... values) {  
  62.         Object list = persistence.get(key);  
  63.         if (list == null)  
  64.             list = new LinkedList<Object>();  
  65.         else  
  66.             list = serializer.unmarshal(list);  
  67.           
  68.         LinkedList<Object> listRef = (LinkedList<Object>) list;  
  69.         for (Object value : values)  
  70.             listRef.addLast(value);  
  71.         persistence.put(key, serializer.marshal(list));  
  72.     }  
  73.       
  74.     public Object[] LRange(String key, int start, int end) {  
  75.         Object list = persistence.get(key);  
  76.         if (list == null)  
  77.             return NULL;  
  78.           
  79.         LinkedList<Object> listRef = (LinkedList<Object>) serializer.unmarshal(list);  
  80.         if (end > listRef.size())  
  81.             end = listRef.size();  
  82.         return listRef.subList(start, end).toArray();  
  83.     }  
  84.       
  85.       
  86.     // =================================================  
  87.     //                  Unsorted Set value  
  88.     // =================================================  
  89.   
  90.     public void SAdd(String key, Object... values) {  
  91.         Object set = persistence.get(key);  
  92.         if (set == null)  
  93.             set = new HashSet<Object>();  
  94.         else  
  95.             set = serializer.unmarshal(set);  
  96.           
  97.         HashSet<Object> setRef = (HashSet<Object>) set;  
  98.         for (Object value : values)  
  99.             setRef.add(value);  
  100.         persistence.put(key, serializer.marshal(set));  
  101.     }  
  102.       
  103.     public Object[] SMembers(String key) {  
  104.         Object set = persistence.get(key);  
  105.         if (set == null)  
  106.             return NULL;  
  107.           
  108.         set = serializer.unmarshal(set);  
  109.         return ((HashSet<Object>) set).toArray();  
  110.     }  
  111.       
  112.     public Object[] SInter(String key1, String key2) {  
  113.         Object set1 = persistence.get(key1);  
  114.         Object set2 = persistence.get(key2);  
  115.         if (set1 == null || set2 == null)  
  116.             return NULL;  
  117.           
  118.         HashSet<Object> set1Ref = (HashSet<Object>) serializer.unmarshal(set1);  
  119.         HashSet<Object> set2Ref = (HashSet<Object>) serializer.unmarshal(set2);  
  120.         set1Ref.retainAll(set2Ref);  
  121.         return set1Ref.toArray();  
  122.     }  
  123.       
  124.     public Object[] SDiff(String key1, String key2) {  
  125.         Object set1 = persistence.get(key1);  
  126.         Object set2 = persistence.get(key2);  
  127.         if (set1 == null || set2 == null)  
  128.             return NULL;  
  129.           
  130.         HashSet<Object> set1Ref = (HashSet<Object>) serializer.unmarshal(set1);  
  131.         HashSet<Object> set2Ref = (HashSet<Object>) serializer.unmarshal(set2);  
  132.         set1Ref.removeAll(set2Ref);  
  133.         return set1Ref.toArray();  
  134.     }  
  135.       
  136.       
  137.     // =================================================  
  138.     //                  Sorted Set value  
  139.     // =================================================  
  140.   
  141.     public void ZAdd(String key, Object... values) {  
  142.         Object set = persistence.get(key);  
  143.         if (set == null)  
  144.             set = new TreeSet<Object>();  
  145.         else  
  146.             set = serializer.unmarshal(set);  
  147.           
  148.         TreeSet<Object> setRef = (TreeSet<Object>) set;  
  149.         for (Object value : values)  
  150.             setRef.add(value);  
  151.         persistence.put(key, serializer.marshal(set));  
  152.     }  
  153.       
  154.     public Object[] SRange(String key, Object from) {  
  155.         Object set = persistence.get(key);  
  156.         if (set == null)  
  157.             return NULL;  
  158.           
  159.         set = serializer.unmarshal(set);  
  160.         return ((TreeSet<Object>) set).tailSet(from).toArray();  
  161.     }  
  162.       
  163. }  
[java]  view plain  copy
  1. package com.cdai.studio.redis;  
  2.   
  3. import java.io.ByteArrayInputStream;  
  4. import java.io.ByteArrayOutputStream;  
  5. import java.io.IOException;  
  6. import java.io.ObjectInputStream;  
  7. import java.io.ObjectOutputStream;  
  8. import java.io.Serializable;  
  9. import java.util.Arrays;  
  10.   
  11. class Serializer {  
  12.   
  13.     Object marshal(Object object) {  
  14.         if (object == null)  
  15.             return null;  
  16.         return new BytesWrapper((Serializable) object);  
  17.     }  
  18.       
  19.     Object unmarshal(Object object) {  
  20.         if (object == null)  
  21.             return null;  
  22.         return ((BytesWrapper) object).readObject();  
  23.     }  
  24.       
  25. }  
  26.   
  27.   
  28. class BytesWrapper {  
  29.       
  30.     private byte[] bytes;  
  31.       
  32.     <T extends Serializable> BytesWrapper(T object) {  
  33.         writeBytes(object);  
  34.     }  
  35.       
  36.     <T extends Serializable> void writeBytes(T object) {  
  37.         try {  
  38.             ByteArrayOutputStream buffer = new ByteArrayOutputStream();  
  39.             ObjectOutputStream output = new ObjectOutputStream(buffer);  
  40.             output.writeObject(object);  
  41.             output.flush();  
  42.             bytes = buffer.toByteArray();  
  43.             output.close();  
  44.         }  
  45.         catch (IOException e) {  
  46.             e.printStackTrace();  
  47.             throw new IllegalStateException(e);  
  48.         }  
  49.     }  
  50.       
  51.     Object readObject() {  
  52.         try {  
  53.             ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream(bytes));  
  54.             Object object = input.readObject();  
  55.             input.close();  
  56.             return object;  
  57.         }  
  58.         catch (Exception e) {  
  59.             e.printStackTrace();  
  60.             throw new IllegalStateException(e);  
  61.         }  
  62.     }  
  63.   
  64.     @Override  
  65.     public int hashCode() {  
  66.         final int prime = 31;  
  67.         int result = 1;  
  68.         result = prime * result + Arrays.hashCode(bytes);  
  69.         return result;  
  70.     }  
  71.   
  72.     @Override  
  73.     public boolean equals(Object obj) {  
  74.         if (this == obj)  
  75.             return true;  
  76.         if (obj == null)  
  77.             return false;  
  78.         if (getClass() != obj.getClass())  
  79.             return false;  
  80.         BytesWrapper other = (BytesWrapper) obj;  
  81.         if (!Arrays.equals(bytes, other.bytes))  
  82.             return false;  
  83.         return true;  
  84.     }  
  85.       
  86. }  
[java]  view plain  copy
  1. package com.cdai.studio.redis;  
  2.   
  3. import java.util.HashMap;  
  4.   
  5. class Persistence {  
  6.   
  7.     private HashMap<String, Object> storage =  
  8.         new HashMap<String, Object>();  
  9.       
  10.       
  11.     void put(String key, Object value) {  
  12.         storage.put(key, value);  
  13.     }  
  14.       
  15.     Object get(String key) {  
  16.         return storage.get(key);  
  17.     }  
  18.       
  19. }  


3.简单的客户端
[java]  view plain  copy
  1. package com.cdai.studio.redis;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.ObjectInputStream;  
  5. import java.io.ObjectOutputStream;  
  6. import java.net.ServerSocket;  
  7. import java.net.Socket;  
  8. import java.util.List;  
  9.   
  10. public class RedisServer {  
  11.   
  12.     private RedisDB redis;  
  13.       
  14.     public RedisServer(RedisDB redis) {  
  15.         this.redis = redis;  
  16.     }  
  17.       
  18.     @SuppressWarnings("unchecked")  
  19.     public void start() {  
  20.         ServerSocket serverSocket = null;  
  21.         try {  
  22.             serverSocket = new ServerSocket(1234);  
  23.             while (true) {  
  24.                 Socket socket = serverSocket.accept();  
  25.                   
  26.                 ObjectInputStream input = new ObjectInputStream(socket.getInputStream());  
  27.                 List<Object> request = (List<Object>) input.readObject();  
  28.                   
  29.                 Object response = null;  
  30.                 if ("Set".equals(request.get(0))) {  
  31.                     redis.Set((String) request.get(1), request.get(2));  
  32.                 }  
  33.                 else if ("Get".equals(request.get(0))) {  
  34.                     response = redis.Get((String) request.get(1));  
  35.                 }  
  36.                   
  37.                 ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());  
  38.                 output.writeObject(response);  
  39.                   
  40.                 input.close();  
  41.                 output.close();  
  42.                 socket.close();  
  43.             }  
  44.         }   
  45.         catch (Exception e) {  
  46.             e.printStackTrace();  
  47.         }  
  48.         finally {  
  49.             if (serverSocket != null) {  
  50.                 try {  
  51.                     serverSocket.close();  
  52.                 } catch (IOException e) {  
  53.                 }  
  54.             }  
  55.         }  
  56.           
  57.     }  
  58.       
  59. }  
[java]  view plain  copy
  1. package com.cdai.studio.redis;  
  2.   
  3. import java.io.ObjectInputStream;  
  4. import java.io.ObjectOutputStream;  
  5. import java.io.Serializable;  
  6. import java.net.Socket;  
  7. import java.util.Arrays;  
  8. import java.util.List;  
  9.   
  10. public class RedisClient {  
  11.   
  12.     public <T extends Serializable> void Set(String key, Object value) {  
  13.         sendRequest(Arrays.asList("Set", key, value));  
  14.     }  
  15.       
  16.     public Object Get(String key) {  
  17.         return sendRequest(Arrays.<Object>asList("Get", key));  
  18.     }  
  19.       
  20.     private Object sendRequest(List<Object> payload) {  
  21.         Socket socket = null;  
  22.         try {  
  23.             socket = new Socket("localhost"1234);  
  24.               
  25.             ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());  
  26.             output.writeObject(payload);  
  27.             output.flush();  
  28.               
  29.             ObjectInputStream input = new ObjectInputStream(socket.getInputStream());  
  30.             Object response = input.readObject();  
  31.               
  32.             output.close();  
  33.             input.close();  
  34.             return response;  
  35.         } catch (Exception e) {  
  36.             e.printStackTrace();  
  37.         } finally {  
  38.             if (socket != null) {  
  39.                 try {  
  40.                     socket.close();  
  41.                 } catch (Exception e) {  
  42.                 }  
  43.             }  
  44.         }  
  45.         return null;  
  46.     }  
  47.       
  48. }  


4.实现简单的Twitter
[java]  view plain  copy
  1. package com.cdai.studio.redis;  
  2.   
  3. import java.util.Arrays;  
  4.   
  5. public class RedisTest {  
  6.   
  7.     public static void main(String[] args) {  
  8.           
  9.         RedisDB redis = new RedisDB();  
  10.           
  11.         // 1.Create user follow relationship  
  12.         redis.SAdd("users""A""B""C");  
  13.           
  14.         // User A follows B, C  
  15.         redis.SAdd("users:A:following""B""C");  
  16.         redis.SAdd("users:B:followers""A");  
  17.         redis.SAdd("users:C:followers""A");  
  18.           
  19.         // User C follows B   
  20.         redis.SAdd("users:C:following""B");  
  21.         redis.SAdd("users:B:followers""C");  
  22.           
  23.           
  24.         // 2.1 B send tweet  
  25.         int tid = redis.Incr("tweets:next_id");  
  26.         redis.Set("tweets:" + tid, "B publish hello");  
  27.         redis.LPush("global:timeline", tid);  
  28.         redis.LPush("users:B:timeline", tid);  
  29.         for (Object follower : redis.SMembers("users:B:followers"))  
  30.             redis.LPush("users:" + follower + ":timeline", tid);  
  31.           
  32.         // 2.2 C send tweet   
  33.         tid = redis.Incr("tweets:next_id");  
  34.         redis.Set("tweets:" + tid, "C publish world");  
  35.         redis.LPush("global:timeline", tid);  
  36.         redis.LPush("users:C:timeline", tid);  
  37.         for (Object follower : redis.SMembers("users:C:followers"))  
  38.             redis.LPush("users:" + follower + ":timeline", tid);  
  39.                   
  40.           
  41.         Object[] tids = redis.LRange("global:timeline"09);  
  42.         String[] tweetids = new String[tids.length];  
  43.         for (int i = 0; i < tids.length; i++)  
  44.             tweetids[i] = "tweets:" + tids[i];  
  45.         System.out.println(Arrays.toString(redis.MGet(tweetids)));  
  46.     }  
  47.   
  48. }  


5.需要注意的问题

byte数组的equals和hashcode默认实现比较对象地址的,要借助于Arrays的equals和hashcode方法。

String字符串序列化和反序列化时要注意编码格式的问题,编码解码时应该使用相同的编码。

HashSet上的操作,removeAll补集,retainAll交集,addAll并集。


6.更加强大的Redis

Redis自己实现了各种数据结构,可以非常方便地增删改查,并且效率很高。这里我们只是用
Java来简单的学习了下Redis基本功能,其实Redis还支持很多其他的高级功能, 如消息订阅、
数据过期设置、事务、数据持久化。想要进一步学习的话可以试着用Java实现它们。

你可能感兴趣的:(Java版的Redis)