我们有一个通过RMI进行通信的客户端/服务器应用程序。服务器将HashMaps发送到客户端。一切正常,但是当发送大型HashMaps时,传输时间可能很慢。
有没有办法在发送之前压缩HashMaps,然后在客户端上解压缩?我不想在磁盘上创建任何文件(所有必须在RAM中)
由于
答案 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 :)所以试试这个方法。