序列化压缩实现及对比测试
网上介绍序列化压缩的用gzip比较多。写个测试代码,测试一下四种序列化方式:
- 无压缩
- zlib压缩
- gzip压缩
- zip压缩
测例结果显示压缩效果:gzip压缩 > zlib压缩 > zip压缩 > 无压缩
测例结果显示压缩速度:zlib压缩 > gzip压缩 > zip压缩 = 无压缩
zlib、gzip、zip介绍
1. zlib
zlib是一个开源库,提供了在内存中压缩和解压的函数。zlib它的设计目标是处理单纯的数据(而不管数据的来源是什么)。
2. gzip
gzip是UNIX下的一种数据格式(.tar.gz)。gzip是在zlib之上,包了一层,在头和尾添加了一些额外的信息。
gzip是一种文件压缩工具(或该压缩工具产生的压缩文件格式),它的设计目标是处理单个的文件。gzip在压缩文件中的数据时使用的就是zlib。为了保存与文件属性有关的信息,gzip需要在压缩文件(*.gz)中保存更多的头信息内容,而zlib不用考虑这一点。但gzip只适用于单个文件,所以我们在UNIX/Linux上经常看到的压缩包后缀都是*.tar.gz或*.tgz,也就是先用tar把多个文件打包成单个文件,再用gzip压缩的结果。
3. zip
zip只是一种数据结构,跟rar同级别的。zip是适用于压缩多个文件的格式(相应的工具有PkZip和WinZip等),因此,zip文件还要进一步包含文件目录结构的信息,比gzip的头信息更多。但需要注意,zip格式可采用多种压缩算法,我们常见的zip文件大多不是用zlib的算法压缩的,其压缩数据的格式与gzip大不一样。
Java SDK提供了对上述三种压缩技术的支持:Inflater类和Deflater类直接用zlib库对数据压缩/解压缩,GZIPInputStream类和GZIPOutputStream类提供了对gzip格式的支持,ZipFile、ZipInputStream、ZipOutputStream则用于处理zip格式的文件。
所以,你应当根据你的具体需求,选择不同的压缩技术:如果只需要压缩/解压缩数据,你可以直接用zlib实现,如果需要生成gzip格式的文件或解压其他工具的压缩结果,你就必须用gzip或zip等相关的类来处理了。
测试代码
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.util.zip.Inflater; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; public class TestCompressedSerializaion { public static BigObject createBigObject() { final int SIZE = 1 << 12; int[] bigArray = new int[SIZE]; for (int i = 0; i < SIZE; ++i) { bigArray[i] = (int) (Math.random() * 100); } return new BigObject(bigArray); } public static void serializeObject(Object obj, String flName) { FileOutputStream fileOut = null; ObjectOutputStream out = null; try { fileOut = new FileOutputStream(flName); out = new ObjectOutputStream(fileOut); out.writeObject(obj); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { try { out.close(); } catch (Exception e) { } } if (fileOut != null) { try { fileOut.close(); } catch (Exception e) { } } } } public static Object deserializeObject(String flName) { FileInputStream fileIn = null; ObjectInputStream in = null; Object res = null; try { fileIn = new FileInputStream(flName); in = new ObjectInputStream(fileIn); res = in.readObject(); } catch (IOException ioe) { ioe.printStackTrace(); } catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (Exception e) { } } if (fileIn != null) { try { fileIn.close(); } catch (Exception e) { } } } return res; } public static void testCompressedSerializaion(CompressType compressType, BigObject bigObj) { CompressedSerializaion.compressType = compressType; String flName = compressType.name() + ".ser"; long beginTime = System.currentTimeMillis(); serializeObject(new CompressedSerializaion(bigObj), flName); CompressedSerializaion obj = (CompressedSerializaion) deserializeObject(flName); long usedTime = System.currentTimeMillis()-beginTime; System.out.println(flName + " length:" + new File(flName).length() + " usedTime:"+usedTime + " serialization correctness:"+ bigObj.equals(obj.getBigObj())); } public static void main(String[] args) { BigObject bigObj = createBigObject(); testCompressedSerializaion(CompressType.UNCOMPRESSED, bigObj); testCompressedSerializaion(CompressType.ZLIB, bigObj); testCompressedSerializaion(CompressType.GZIP, bigObj); testCompressedSerializaion(CompressType.ZIP, bigObj); } } enum CompressType { UNCOMPRESSED, ZLIB, GZIP, ZIP }; @SuppressWarnings("serial") class BigObject implements Serializable { private int[] bigArray; public BigObject(int[] bigArray) { this.bigArray = bigArray; } public int[] getBigArray() { return bigArray; } public void setBigArray(int[] bigArray) { this.bigArray = bigArray; } @Override public int hashCode() { return Arrays.hashCode(bigArray); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; BigObject other = (BigObject) obj; if (!Arrays.equals(bigArray, other.bigArray)) return false; return true; } } @SuppressWarnings("serial") class CompressedSerializaion implements Serializable { public static CompressType compressType = CompressType.UNCOMPRESSED; private BigObject bigObj; public CompressedSerializaion(BigObject bigObj) { this.bigObj = bigObj; } public BigObject getBigObj() { return bigObj; } public void setBigObj(BigObject bigObj) { this.bigObj = bigObj; } @Override public int hashCode() { return bigObj == null ? 0 : bigObj.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; CompressedSerializaion other = (CompressedSerializaion) obj; if (!bigObj.equals(other.bigObj)) return false; return true; } public void writeMyObject(ObjectOutputStream out) throws IOException { out.writeObject(bigObj); } private void readMyObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { bigObj = (BigObject) ois.readObject(); } private void writeObject(ObjectOutputStream out) throws IOException { if (compressType == CompressType.UNCOMPRESSED) { writeMyObject(out); } else if (compressType == CompressType.ZLIB) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream baOos = new ObjectOutputStream(baos); writeMyObject(baOos); baOos.flush(); baOos.close(); byte unCompressedBytes[] = baos.toByteArray(); baos.close(); Deflater compresser = new Deflater(Deflater.BEST_SPEED); compresser.setInput(unCompressedBytes); compresser.finish(); byte bytes[] = new byte[unCompressedBytes.length]; int nz = compresser.deflate(bytes); out.writeInt(nz); out.write(bytes, 0, nz); } else { ByteArrayOutputStream baos = new ByteArrayOutputStream(); if (compressType == CompressType.GZIP) { GZIPOutputStream gzos = new GZIPOutputStream(baos); ObjectOutputStream gzOos = new ObjectOutputStream(gzos); writeMyObject(gzOos); gzOos.flush(); gzos.close(); } else { ZipOutputStream zos = new ZipOutputStream(baos); ZipEntry ze = new ZipEntry("testZipSerializaion"); zos.setLevel(Deflater.BEST_SPEED); zos.putNextEntry(ze); ObjectOutputStream zipOos = new ObjectOutputStream(zos); writeMyObject(zipOos); zipOos.flush(); zos.closeEntry(); zos.close(); } byte bytes[] = baos.toByteArray(); int nz = baos.size(); baos.close(); out.writeInt(nz); out.write(bytes, 0, nz); } } private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { if (compressType == CompressType.UNCOMPRESSED) { readMyObject(ois); } else { int nz = ois.readInt(); byte bytes[] = new byte[nz]; int totalSize = 0; while (totalSize < nz) { // attempt to read the entire buffer in a single read int bytesRead = ois.read(bytes, totalSize, (nz - totalSize)); if (bytesRead == -1) // EOF break; totalSize += bytesRead; } if (compressType == CompressType.ZLIB) { Inflater decompresser = new Inflater(); decompresser.setInput(bytes, 0, nz); byte[] unCompressedBytes; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { byte[] buf = new byte[1024]; while (!decompresser.finished()) { int i = decompresser.inflate(buf); baos.write(buf, 0, i); } unCompressedBytes = baos.toByteArray(); } catch (DataFormatException e) { throw new IOException(e); } finally { try { baos.close(); decompresser.end(); } finally { } } ByteArrayInputStream bais = new ByteArrayInputStream( unCompressedBytes); ObjectInputStream baOis = new ObjectInputStream(bais); readMyObject(baOis); bais.close(); } else if (compressType == CompressType.GZIP) { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); GZIPInputStream zis = new GZIPInputStream(bais); ObjectInputStream zipOis = new ObjectInputStream(zis); readMyObject(zipOis); bais.close(); } else if (compressType == CompressType.ZIP) { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); ZipInputStream zis = new ZipInputStream(bais); // open first zip file entry ZipEntry ze = zis.getNextEntry(); ObjectInputStream zipOis = new ObjectInputStream(zis); readMyObject(zipOis); bais.close(); } } } }
测试结果
UNCOMPRESSED.ser length:16582 usedTime:14 serialization correctness:true ZLIB.ser length:6108 usedTime:4 serialization correctness:true GZIP.ser length:5682 usedTime:5 serialization correctness:true ZIP.ser length:6254 usedTime:14 serialization correctness:true