Java版的Redis

java

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


代码的组织结构如下图:




package com.cdai.studio.redis;

import java.util.HashSet;

import java.util.LinkedList;

import java.util.TreeSet;

@SuppressWarnings("unchecked")

public class RedisDB {

private Persistence persistence = new Persistence();

private Serializer serializer = new Serializer();

private static final Object[] NULL = new Object[0];

// =================================================

// String value

// =================================================

public void Set(String key, Object value) {

persistence.put(key, serializer.marshal(value));

}

public Object Get(String key) {

return serializer.unmarshal(persistence.get(key));

}

public Object[] MGet(String... keys) {

Object[] values = new Object[keys.length];

for (int i = 0; i < keys.length; i++)

values[i] = Get(keys[i]);

return values;

}

public int Incr(String key) {

Object value = Get(key);

Integer valueRef = (value == null) ? 1 : (Integer) value;

Set(key, valueRef + 1);

return valueRef;

}

// =================================================

// List value

// =================================================

public void LPush(String key, Object... values) {

Object list = persistence.get(key);

if (list == null)

list = new LinkedList<Object>();

else

list = serializer.unmarshal(list);

LinkedList<Object> listRef = (LinkedList<Object>) list;

for (Object value : values)

listRef.addFirst(value);

persistence.put(key, serializer.marshal(list));

}

public void RPush(String key, Object... values) {

Object list = persistence.get(key);

if (list == null)

list = new LinkedList<Object>();

else

list = serializer.unmarshal(list);

LinkedList<Object> listRef = (LinkedList<Object>) list;

for (Object value : values)

listRef.addLast(value);

persistence.put(key, serializer.marshal(list));

}

public Object[] LRange(String key, int start, int end) {

Object list = persistence.get(key);

if (list == null)

return NULL;

LinkedList<Object> listRef = (LinkedList<Object>) serializer.unmarshal(list);

if (end > listRef.size())

end = listRef.size();

return listRef.subList(start, end).toArray();

}

// =================================================

// Unsorted Set value

// =================================================

public void SAdd(String key, Object... values) {

Object set = persistence.get(key);

if (set == null)

set = new HashSet<Object>();

else

set = serializer.unmarshal(set);

HashSet<Object> setRef = (HashSet<Object>) set;

for (Object value : values)

setRef.add(value);

persistence.put(key, serializer.marshal(set));

}

public Object[] SMembers(String key) {

Object set = persistence.get(key);

if (set == null)

return NULL;

set = serializer.unmarshal(set);

return ((HashSet<Object>) set).toArray();

}

public Object[] SInter(String key1, String key2) {

Object set1 = persistence.get(key1);

Object set2 = persistence.get(key2);

if (set1 == null || set2 == null)

return NULL;

HashSet<Object> set1Ref = (HashSet<Object>) serializer.unmarshal(set1);

HashSet<Object> set2Ref = (HashSet<Object>) serializer.unmarshal(set2);

set1Ref.retainAll(set2Ref);

return set1Ref.toArray();

}

public Object[] SDiff(String key1, String key2) {

Object set1 = persistence.get(key1);

Object set2 = persistence.get(key2);

if (set1 == null || set2 == null)

return NULL;

HashSet<Object> set1Ref = (HashSet<Object>) serializer.unmarshal(set1);

HashSet<Object> set2Ref = (HashSet<Object>) serializer.unmarshal(set2);

set1Ref.removeAll(set2Ref);

return set1Ref.toArray();

}

// =================================================

// Sorted Set value

// =================================================

public void ZAdd(String key, Object... values) {

Object set = persistence.get(key);

if (set == null)

set = new TreeSet<Object>();

else

set = serializer.unmarshal(set);

TreeSet<Object> setRef = (TreeSet<Object>) set;

for (Object value : values)

setRef.add(value);

persistence.put(key, serializer.marshal(set));

}

public Object[] SRange(String key, Object from) {

Object set = persistence.get(key);

if (set == null)

return NULL;

set = serializer.unmarshal(set);

return ((TreeSet<Object>) set).tailSet(from).toArray();

}

}

package com.cdai.studio.redis;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

import java.util.Arrays;

class Serializer {

Object marshal(Object object) {

if (object == null)

return null;

return new BytesWrapper((Serializable) object);

}

Object unmarshal(Object object) {

if (object == null)

return null;

return ((BytesWrapper) object).readObject();

}

}

class BytesWrapper {

private byte[] bytes;

<T extends Serializable> BytesWrapper(T object) {

writeBytes(object);

}

<T extends Serializable> void writeBytes(T object) {

try {

ByteArrayOutputStream buffer = new ByteArrayOutputStream();

ObjectOutputStream output = new ObjectOutputStream(buffer);

output.writeObject(object);

output.flush();

bytes = buffer.toByteArray();

output.close();

}

catch (IOException e) {

e.printStackTrace();

throw new IllegalStateException(e);

}

}

Object readObject() {

try {

ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream(bytes));

Object object = input.readObject();

input.close();

return object;

}

catch (Exception e) {

e.printStackTrace();

throw new IllegalStateException(e);

}

}

@Override

public int hashCode() {

final int prime = 31;

int result = 1;

result = prime * result + Arrays.hashCode(bytes);

return result;

}

@Override

public boolean equals(Object obj) {

if (this == obj)

return true;

if (obj == null)

return false;

if (getClass() != obj.getClass())

return false;

BytesWrapper other = (BytesWrapper) obj;

if (!Arrays.equals(bytes, other.bytes))

return false;

return true;

}

}

package com.cdai.studio.redis;

import java.util.HashMap;

class Persistence {

private HashMap<String, Object> storage =

new HashMap<String, Object>();

void put(String key, Object value) {

storage.put(key, value);

}

Object get(String key) {

return storage.get(key);

}

}



3.简单的客户端

package com.cdai.studio.redis;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.List;

public class RedisServer {

private RedisDB redis;

public RedisServer(RedisDB redis) {

this.redis = redis;

}

@SuppressWarnings("unchecked")

public void start() {

ServerSocket serverSocket = null;

try {

serverSocket = new ServerSocket(1234);

while (true) {

Socket socket = serverSocket.accept();

ObjectInputStream input = new ObjectInputStream(socket.getInputStream());

List<Object> request = (List<Object>) input.readObject();

Object response = null;

if ("Set".equals(request.get(0))) {

redis.Set((String) request.get(1), request.get(2));

}

else if ("Get".equals(request.get(0))) {

response = redis.Get((String) request.get(1));

}

ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());

output.writeObject(response);

input.close();

output.close();

socket.close();

}

}

catch (Exception e) {

e.printStackTrace();

}

finally {

if (serverSocket != null) {

try {

serverSocket.close();

} catch (IOException e) {

}

}

}

}

}

package com.cdai.studio.redis;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

import java.net.Socket;

import java.util.Arrays;

import java.util.List;

public class RedisClient {

public <T extends Serializable> void Set(String key, Object value) {

sendRequest(Arrays.asList("Set", key, value));

}

public Object Get(String key) {

return sendRequest(Arrays.<Object>asList("Get", key));

}

private Object sendRequest(List<Object> payload) {

Socket socket = null;

try {

socket = new Socket("localhost", 1234);

ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());

output.writeObject(payload);

output.flush();

ObjectInputStream input = new ObjectInputStream(socket.getInputStream());

Object response = input.readObject();

output.close();

input.close();

return response;

} catch (Exception e) {

e.printStackTrace();

} finally {

if (socket != null) {

try {

socket.close();

} catch (Exception e) {

}

}

}

return null;

}

}



4.实现简单的Twitter

package com.cdai.studio.redis;

import java.util.Arrays;

public class RedisTest {

public static void main(String[] args) {

RedisDB redis = new RedisDB();

// 1.Create user follow relationship

redis.SAdd("users", "A", "B", "C");

// User A follows B, C

redis.SAdd("users:A:following", "B", "C");

redis.SAdd("users:B:followers", "A");

redis.SAdd("users:C:followers", "A");

// User C follows B

redis.SAdd("users:C:following", "B");

redis.SAdd("users:B:followers", "C");

// 2.1 B send tweet

int tid = redis.Incr("tweets:next_id");

redis.Set("tweets:" + tid, "B publish hello");

redis.LPush("global:timeline", tid);

redis.LPush("users:B:timeline", tid);

for (Object follower : redis.SMembers("users:B:followers"))

redis.LPush("users:" + follower + ":timeline", tid);

// 2.2 C send tweet

tid = redis.Incr("tweets:next_id");

redis.Set("tweets:" + tid, "C publish world");

redis.LPush("global:timeline", tid);

redis.LPush("users:C:timeline", tid);

for (Object follower : redis.SMembers("users:C:followers"))

redis.LPush("users:" + follower + ":timeline", tid);

Object[] tids = redis.LRange("global:timeline", 0, 9);

String[] tweetids = new String[tids.length];

for (int i = 0; i < tids.length; i++)

tweetids[i] = "tweets:" + tids[i];

System.out.println(Arrays.toString(redis.MGet(tweetids)));

}

}



5.需要注意的问题


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


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


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



6.更加强大的Redis


Redis自己实现了各种数据结构,可以非常方便地增删改查,并且效率很高。这里我们只是用

Java来简单的学习了下Redis基本功能,其实Redis还支持很多其他的高级功能,如消息订阅、

数据过期设置、事务、数据持久化。想要进一步学习的话可以试着用Java实现它们。



以上是 Java版的Redis 的全部内容, 来源链接: utcz.com/z/391332.html

回到顶部