如何将十六进制字符串存为二进制数组以节约存储空间

如果有这么一种场景,需要将客户端机器上所有文件的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二进制转十六进制、二进制和十六进制转换,以便于您获取更多的相关知识。

时间: 2024-08-04 01:50:01

如何将十六进制字符串存为二进制数组以节约存储空间的相关文章

java中byte数组与十六进制字符串相互转换

最近在做加密算法的研究和使用,经常会用到byte数组和十六进制字符串的转换.之前对于此类问题我一般都是使用BigInteger这个类转换一下算了,这样为了看输出不是乱码.这其实都不是根本上的解决方案. 最简单的转换方法: /** * @see 将byte[]数组转换为String字符串 * @author Herman.Xiong * @date 2014年5月5日 17:15:42 * @param data byte数组 * @return String 转换后的字符串 */ public

CString字符串转换为十六进制字符串

最近在做项目时遇到这个问题,比如将汉字"冲"转换后为 "51B2",程序使用的是unicode字符集,下面是通过查资料后整理的解决方法: //--------------------------------------------------------------------- //函数:W2C //功能:将16位wchar_t转换为 8位char[2] //参数:w_cn为待转换的16位字符,c_cn[]为转换后的8位字符 //备注:wchar_t的高位字节应该

遍历-一个json格式的字符串,在转换为数组时出现问题

问题描述 一个json格式的字符串,在转换为数组时出现问题 [{"id":"17","name":"w","sex":"a","age":"1"},{"id":"23","name":"a","sex":"e","age

将一个十六进制字符串转换为十进制数值的问题

在程序中,我们有时需要将一个十六进制字符串转换为十进制数字.比如:char *ptr="0x11";int n=0;//我们想让n等于0x11,即17 通常我们在C中,想将一个字符串转换为一整形数字,通常会使用下面的方法:   char *ptr="123";       int n=0;   n=atoi(ptr);   printf("%d/n",n);   //输出:123     但是atoi库函数只能将十进制字符串转化为int整形,比如

c语言-C语言中,我想依次读入两个非常大的数,用字符串存,

问题描述 C语言中,我想依次读入两个非常大的数,用字符串存, C语言中,我想依次读入两个非常大的整数,用字符串存,应该怎么写读入语句,并且想计算他们的和,并输出结果,应该怎么办(这两个整数非常大,long int也不能存) 解决方案 /********** main function *********/ /* ** FILE: tbigint.c ** NOTE: 2015-10-08 created by Jack Liu */ #include<stdio.h> #include<

pb代码解答-PB 如何截取字符串分别放入数组中

问题描述 PB 如何截取字符串分别放入数组中 "三厂,二厂,一厂" 我的里面有三个内容我想要取成 软件分厂 二厂 一厂 将这三个值放进数组 求大神解答 解决方案 string str =substring(字符串,开始位置,截取长度); 解决方案二: 采用以下函数 global type gf_split_parm from function_object end type forward prototypes global function integer gf_split_parm

把普通字符串转换成二进制字符串的函数写出来了,和大家分享一下。

二进制|函数|转换|字符串 '普通字符串转换成二进制字符串函数Function Str2Bin(String)  Dim i, tmpbin  For i=1 to strLength(String)    tmpbin = tmpbin & ChrB(Asc(Mid(String,I,1)))  Next  Str2Bin = tmpbinEnd Function '以下函数来自ChinaASP,计算字符串的真正字节数(支持中文)Function strLength(str)   If (le

支持中文的把普通字符串转成二进制字符串的函数

二进制|函数|中文|字符串 上次的函数有误,这次的才正确支持中文. '把普通字符串转成二进制字符串Function str2bin(varstr)   str2bin=""   For i=1 To Len(varstr)       varchar=mid(varstr,i,1)       varasc = Asc(varchar)       If varasc<0 Then          varasc = varasc + 65535       End If    

从字符串中获得电子邮件数组

数组|字符串 '从字符串中获得电子邮件数组 Function GetMailListFromStrStream(ByVal StrStream As String) As ArrayList Dim Re As System.Text.RegularExpressions.Regex Dim RegStr As String = "[a-zA-Z0-9]+@([a-zA-Z0-9]+\.)+[a-zA-Z0-9]+" '电子邮件 Dim MailList As System.Text.