如果有这么一种场景,需要将客户端机器上所有文件的MD5值,都放到服务端的数据库中,服务端会定期的对客户端的文件进行检查,看是否有非法文件(注:这里用MD5做非法文件的检查,并不是说每个文件的MD5是唯一的,请查看这篇BLOG:不同文件也可以有相同的MD5校验值)。但是为了增加检查的速度,于是就准备把服务端数据库中所有的MD5都加载到CACHE中,可是这里有一个问题,服务端的CACHE机器的内存不够大,不能够完全存放下所有的MD5串,大约可以存放75%左右的数据,并且限于其它的原因也不可能加大CACHE机的内存,也不可能增加机器,只有这么一台机器。
鉴于这种情况,通常的处理方式是将部份数据加载到CACHE中,然后对客户端发过来不存在于CACHE中的MD5串再通过查数据比较,这种方式也是一解方案,至少75%的数据可以通过CACHE获取到,其它的25%再从数据库中查询了。但是这里还有一个问题就是服务端保存的MD5文件有很多,如有数千万条,即使只有25%的数据到数据库中查询,但是每次都是从数千万条数据中去查询一条,虽然存在索引,但是也是要费不少时间的。这个时候,可能有些童鞋会想到分库分表,多加CACHE机什么的,但是前面已经说了只有这么一台CACHE机,就不能够解决这个问题了吗?
解决方案肯定是有的,首先我们需要对MD5值的构成有所了解,那就是MD5结果是由0-F这些字符{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }组成的,0-F也是16进制的组成元素。 我们知道16进制的每个字符需要用4位二进制位来表示,Java中byte用二进制表示占用8位,也就是说一个byte可以存放两位16进制数,进一步说 32位的MD5值,就可以存到16个字节中,这样一下就节约了一半的存储空间(实际节约不到一半,不能够简单比较,但是可以这样通俗理解,后面会有比较说明),因为如果是直接将32位的MD5存放字符串,就是占32个字节。以下是将16进制转为字节数组,以及将字节数据转为16进制字符串的JAVA代码实现(注:实现代码是摘自于BLOG:http://franksinger.iteye.com/blog/614540):
* Convert byte[] to hex string.这里我们可以将byte转换成int,然后利用Integer.toHexString(int)来转换成16进制字符串。 * @param src byte[] data * @return hex string */ public static String bytesToHexString(byte[] src){ StringBuilder stringBuilder = new StringBuilder(""); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } /** * Convert hex string to byte[] * @param hexString the hex string * @return byte[] */ public static byte[] hexStringToBytes(String hexString) { if (hexString == null || hexString.equals("")) { return null; } hexString = hexString.toUpperCase(); int length = hexString.length() / 2; char[] hexChars = hexString.toCharArray(); byte[] d = new byte[length]; for (int i = 0; i < length; i++) { int pos = i * 2; d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); } return d; } /** * Convert char to byte * @param c char * @return byte */ private byte charToByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c); }
好了,现在容量的问题是解决了,可以将数据库中全部的数据都读入到内存当中了,鼓掌!为了保证对客户查询的快速响应,我们会将从数据库中读出来的MD5字符串放到HashSet或者是HashMap中,以提供快速查询。可是我们发现下面的代码却达不到我们想要的效果:
Set<byte[]> hasSet = new HashSet<byte[]>(); byte[] k1 = new byte[] { 1, 2, 3 }; byte[] k2 = new byte[] { 1, 2, 3 }; String val = "value"; hasSet.add(k1); Assert.assertTrue(hasSet.contains(k2));
返回栏目页:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/extra/
在做assertTrue()的时候,抛出了异常,这就表示HashSet中没有k2这么一个key,这是因为数组的比较不是按值进行比较,而按引用进入比较的,而这里k2和k1是两个不相同的对象,这也就是HashSet中找不到k2的原因,因为它里面只存了k1。有人在 stackOverFlow上面也提到了这样的问题,有兴趣可以看看了:http://stackoverflow.com/questions/1058149/using-a-byte-array-as-hashmap-key-java。
我们要使用字节数组做为Key,这个时候我们就需要对其进行包装才可以使用,这里有两种包装方案,第一种是使用Java为我们提供了 java.nio.ByteBuffer这个类来包装,第二种方案是使用自己的封装类来包装,下面分别介绍两种实现方式,后面会有两种方式实现比较,究竟哪种更省内存。
1、使用java.nio.ByteBuffer这个类来包装
以下是一个封装了add,remove及contains方法的HashSet实现类:
public class ByteKeyHashSet extends HashSet<ByteBuffer> { private static final long serialVersionUID = -2702041216392736060L; public boolean add(byte[] key) { return super.add(ByteBuffer.wrap(key)); } public boolean add(String key) { return super.add(ByteBuffer.wrap(key.getBytes())); } public boolean remove(byte[] key) { return super.remove(ByteBuffer.wrap(key)); } public boolean remove(String key) { return super.remove(ByteBuffer.wrap(key.getBytes())); } public boolean contains(byte[] key) { return super.contains(ByteBuffer.wrap(key)); } public boolean contains(String key) { return super.contains(ByteBuffer.wrap(key.getBytes())); } }
我们用下面这个单元测试跑一下:
public class ByteKeyHashMapTest extends TestCase{ @Test public void test() { ByteKeyHashSet byteKeyHashSet = new ByteKeyHashSet(); byte[] k1 = new byte[] { 1, 2, 3 }; byte[] k2 = new byte[] { 1, 2, 3 }; byteKeyHashSet.add(k1); Assert.assertTrue(byteKeyHashSet.contains(k2)); byteKeyHashSet.remove(k1); Assert.assertFalse(byteKeyHashSet.contains(k2)); } }
OK,没有问题,可以用字节数组做hash key存放了。
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索字符串
, 文件
, 数据
, 进制
, md5
, cache
, md5 c java md5值相同
, byte 16进制
, 获取文件md5值
, byte转16进制
获取文件的MD5值
二进制转十六进制、十六进制转换二进制、二进制转十六进制算法、java二进制转十六进制、二进制和十六进制转换,以便于您获取更多的相关知识。