Сжатие Java HashMap для отправки через RMI - PullRequest
2 голосов
/ 25 ноября 2010

У нас есть клиент-серверное приложение, которое взаимодействует через RMI.Сервер отправляет HashMaps клиенту.Все работает хорошо, однако при отправке больших HashMaps время передачи может быть медленным.

Есть ли способ сжать HashMaps перед отправкой, а затем распаковать на клиенте?Я не хочу создавать какие-либо файлы на диске вообще (все должно быть в оперативной памяти)

Спасибо

Ответы [ 4 ]

5 голосов
/ 25 ноября 2010

Вы можете использовать DeflatorOutputStream для ByteArrayOutputStream, однако в итоге вы получите байт [], поэтому ваш вызов RMI должен вернуть байт [].

Небольшой сериализуемый объект не будет хорошо сжиматься, однако, если у вас есть много Сериализуемых объектов, он может очень хорошо сжиматься. Так же можно большое количество текста.

Самое простое, что нужно сделать - это попробовать. Если есть повторяющиеся строки или даже части строк, это поможет сжатию.

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 голос
/ 25 ноября 2010

Ничего не делать с хэш-картой. Вместо этого Напишите фабрику пользовательских сокетов , которая сжимает данные, используя DeflaterOutputStream .

0 голосов
/ 25 ноября 2010

Много лет назад я использовал для сериализации объектов в байтовый массив, а затем сжать его. Zip по-прежнему поддерживается Java :), поэтому попробуйте этот метод.

0 голосов
/ 25 ноября 2010

Вы можете попробовать пользовательский механизм сериализации для элементов внутри хэш-карты.

Какую информацию вы отправляете? как выглядит объект внутри?

Даже если использовать механизм по умолчанию и пометить все ненужные атрибуты как переходные, это поможет.

Кроме того, вы можете попытаться отправить данные, которые вы сами сериализировали, до ZipOutputStream , но я бы позволил это как последний ресурс, поскольку двоичный контент не будет слишком сильно сжиматься.

EDIT

Поскольку вы используете только строки, вы можете создать оболочку, пользовательская сериализация которой представляет собой сжатый массив (почти как ответ Питера Лоури), но использование пользовательской сериализации позволит вам инкапсулировать процесс сериализации и заставить его работать несколько прозрачно. "для 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();
    }

}
...