今天看了一个压缩字符串的方式,直接使用的Java8的API实现的。
Http中有三种压缩:
- Zlib:常见类型有zip、rar和7z等。非常流行的文件压缩算法.在Linux 平台应用十分广泛
- Deflate:更高的压缩率,7-zip就是实现的它,它还可以对gzip、PNG、MNG甚至Zip文件进行再次压缩从而得到比zlib压缩更小的大小.
- Gzip:一种无损压缩算法,其基础为Deflate,两者差不多。
我们在Http请求头中可以看到支持的压缩算法,支持哪种就会显示哪种:
Deflate是LZ77与哈弗曼编码的一个组合体。
基本原理是:对于要压缩的文件,首先使用LZ77算法的一个变种进行压缩,对得到的结果再使用哈夫曼编码(根据情况,使用静态哈弗曼编码或动态哈夫曼编码)的方法进行压缩。
那么什么是LZ77呢?
LZ77严格意义上来说不是一种算法,而是一种编码理论。它的核心思路是如果一个串中有两个重复的串,那么只需要知道后面的串与前面串重复的长度和后面串起始字符与前面串起始字符相对于起始位置的距离。
什么玩意,不好理解哈。
来举个例子:
比如字符串“不要啊不要啊停”,可以看到重复字符串有两个:“不要啊”
通过LZ77算法可压缩为“不要啊(4,3)停”,其中4表示重复的字符串起始字符(也就是第二个“不”)到第一个重复字符串起始字符(也就是第一个“不”)的距离,3表示重复字符串的长度。
由此可见,对要压缩的字符串是有要求的,重复度越高的字符串,压缩效果是越好!我通过生成UUID字符串测试了,1000个字符它给我压缩成了950个,啊哈哈,这压了个毛!
那么哈夫曼编码是什么呢?
哈夫曼编码是数据结构课程中一种常见的算法。哈夫曼编码使用变长编码表对源符号进行编码,变长编码表通过一种评估来源符号出现概率的方法得到,出现概率较高的字母使用较短的编码,反之出现概率低的使用较长的编码,这样使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。
好吧,这个我也不明白,数据结构中有讲,但是我没学。。。
下面是工具类代码:
package com.xing.parent.utils; import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.nio.charset.StandardCharsets;import java.util.zip.GZIPInputStream;import java.util.zip.GZIPOutputStream; /** * 字符串压缩与解压缩 * @author xinghua */public class CompressUtils { /** * 使用gzip压缩字符串 * @param str 要压缩的字符串 * @return str */ public static String compress(String str) {if (str == null || str.length() == 0) {return str; } ByteArrayOutputStream out = new ByteArrayOutputStream(); GZIPOutputStream gzip = null;try { gzip = new GZIPOutputStream(out); gzip.write(str.getBytes(StandardCharsets.UTF_8)); }catch (Exception e) { e.printStackTrace();return str; }finally {if (gzip != null) {try { gzip.close(); } catch (IOException e) { e.printStackTrace(); } } }return new sun.misc.BASE64Encoder().encode(out.toByteArray()); } /** * 使用gzip解压缩 * @param compressedStr 要解压缩的字符串 * @return 解压后的字符串 */ public static String unCompress(String compressedStr) {if (compressedStr == null || compressedStr.length() == 0) {return compressedStr; } ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayInputStream inputStream = null; GZIPInputStream gzipInputStream = null; byte[] compressed; String decompressed;try { compressed = new sun.misc.BASE64Decoder().decodeBuffer(compressedStr); inputStream = new ByteArrayInputStream(compressed); gzipInputStream = new GZIPInputStream(inputStream); byte[] buffer = new byte[1024]; int offset = -1;while ((offset = gzipInputStream.read(buffer)) != -1) { out.write(buffer, 0, offset); } decompressed = out.toString(); } catch (IOException e) { e.printStackTrace();return compressedStr; } finally {if (gzipInputStream != null) {try { gzipInputStream.close(); } catch (IOException e) { e.printStackTrace(); } }if (inputStream != null) {try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } }try { out.close(); } catch (IOException e) { e.printStackTrace(); } }return decompressed; } public static void main(String[] args) {for(int i=0;i<10;i++){// String str1 = CommonUtil.getRandomStr(1000); String str1 = "我自己的报文,你自己整一个"; System.out.println("压缩前大小:"+str1.length()); long t1 = System.currentTimeMillis(); String compress = CompressUtils.compress(str1); System.out.println("压缩后大小:"+compress.length()+"|耗时:"+(System.currentTimeMillis()-t1)+"ms->"); long t2 = System.currentTimeMillis(); String unCompress = CompressUtils.unCompress(compress); System.out.println("解压后大小:"+unCompress.length()+"|耗时:"+(System.currentTimeMillis()-t2)+"ms->"); System.out.println("对比压缩前后字符串:"+unCompress.equals(str1)); } }}
结果:
我们的一个单个请求报文有17K,压缩后就可以剩下5K,耗时也在可接受范围,不错!
今天学到了一个压缩算法,工作中还学到了netty通信的水位问题。
按鼠标手指都磨起泡了。。。
五一劳动节快乐!
END