压缩Java HashMap以通过RMI发送

时间:2010-11-25 16:04:29

标签: java compression hashmap rmi

我们有一个通过RMI进行通信的客户端/服务器应用程序。服务器将HashMaps发送到客户端。一切正常,但是当发送大型HashMaps时,传输时间可能很慢。

有没有办法在发送之前压缩HashMaps,然后在客户端上解压缩?我不想在磁盘上创建任何文件(所有必须在RAM中)

由于

4 个答案:

答案 0 :(得分:5)

您可以将DeflatorOutputStream用于ByteArrayOutputStream,但最终会得到一个byte [],因此您的RMI调用应该返回一个byte []。

小的可序列化obejct不能很好地压缩,但是如果你有很多Serializable对象,它可以很好地压缩。大量的文字也可以。

最简单的方法是尝试一下。如果有重复的字符串甚至是字符串的一部分,这将有助于压缩。

public static void main(String... args) throws IOException {
    Map<String, String> map = new HashMap<String, String>();

    for(int i=0;i<1000;i++)
        map.put(""+Math.random(), ""+Math.random());
    byte[] bytes1 = toBytes(map);
    byte[] bytes2 = toCompressedBytes(map);
    System.out.println("HashMap with "+map.size()+" entries, Uncompressed length="+bytes1.length+", compressed length="+bytes2.length);
}

public static byte[] toCompressedBytes(Object o) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(new DeflaterOutputStream(baos));
    oos.writeObject(o);
    oos.close();
    return baos.toByteArray();
}

public static byte[] toBytes(Object o) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(o);
    oos.close();
    return baos.toByteArray();
}

public static Object fromCompressedBytes(byte[] bytes) throws IOException, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(new InflaterInputStream(new ByteArrayInputStream(bytes)));
    return ois.readObject();
}

打印

HashMap with 1000 entries, Uncompressed length=42596, compressed length=19479

答案 1 :(得分:1)

不要对hashmap做任何事情。相反,Write a custom socket factory使用DeflaterOutputStream压缩数据。

答案 2 :(得分:0)

您可以为hashmap中的元素尝试custom serialization mechanism

您发送的是哪种信息?里面的物体怎么样?

<击>

即使使用默认机制,并将所有不需要的属性标记为瞬态也会有所帮助。

此外,您可能会尝试将之前自行序列化的数据发送到ZipOutputStream,但我会将其作为最后一个资源,因为二进制内容不会压缩太多。

修改

由于你只使用字符串,你可以创建一个包装器,其自定义序列化是一个压缩数组(就像Peter Lawrey的回答一样)但是,使用自定义序列化可以让你封装序列化过程并使它工作一些“透明” “对于RMI(RMI序列化永远不会知道你使用的是压缩版本)

这是一个演示:

import java.io.*;
import java.util.*;
import java.util.zip.*;

public class MapDemo implements Serializable { 

    private Map<String,String> map = new HashMap<String,String>();
    // only for demo/comparison purposes, default would use compressoin always
    private boolean useCompression;
    public MapDemo( Map<String,String> map , boolean compressed ) { 
        this.map = map;
        this.useCompression = compressed;
    }

   // This is the custom serialization using compression 
   private void writeObject(ObjectOutputStream out) throws IOException {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();

     OutputStream os = useCompression ?  new DeflaterOutputStream( baos ) : baos;

     ObjectOutputStream oos     = new ObjectOutputStream(  os );
     oos.writeObject( this.map  );
     oos.close();

     out.write( baos.toByteArray() );
   }
}

class Main { 
    public static void main( String [] args )  throws IOException { 
        Map<String,String> regular    = new HashMap<String,String>();
        Map<String,String> compressed = new HashMap<String,String>();
        Random r = new Random();
        for( int i = 0 ; i < 100000 ; i++ ) { 
            String key      = ""+r.nextInt(1000000);
            String value    = ""+r.nextInt(1000000) ;
            // put the same info 
            compressed.put( key , value );
            regular.put( key , value );
        }   
        save( new MapDemo( compressed, true ) , "map.compressed");
        save( new MapDemo( regular, false ) , "map.regular");
    }
    private static void save( Object o, String toFile ) throws IOException  { 
        // This is similar to what RMI serialization would do behind scenes
        ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream(toFile));
        oos.writeObject( o );
        oos.close();
    }

}

答案 3 :(得分:0)

许多年前,我曾经将对象序列化为字节数组,然后将其压缩。 Java仍然支持Zip :)所以试试这个方法。