2010-11-25 31 views
2

Nous avons une application Client/Serveur qui communique via RMI. Le serveur envoie HashMaps au client. Tout fonctionne bien, cependant lors de l'envoi de grandes HashMaps, les temps de transfert peuvent être lents.Compresser Java HashMap à envoyer sur RMI

Y at-il un moyen de compresser les HashMaps avant d'envoyer, puis de décompresser sur le client? Je ne veux pas créer de fichiers sur que ce soit le disque (Tout doit être en RAM)

Merci

+0

quel genre d'objets mettez-vous dans la carte et quel est le nombre moyen d'éléments que vous avez dans la carte? –

+1

La lenteur n'est-elle pas normale pour augmenter les données? Je veux dire que si une carte avec 100 éléments prend 100ms, une carte avec environ 200 éléments ne prendra pas environ 200ms (grosso modo). Avez-vous des chiffres qui indiquent cette lenteur? Je préfère aller partager la requête et l'envoyer au serveur par lots plutôt que de jouer avec le code pour des raisons de performances, à moins qu'il n'y ait absolument aucun moyen de le faire et que votre problème ne permette pas de diviser les requêtes ... –

Répondre

4

Vous pouvez utiliser DeflatorOutputStream à un ByteArrayOutputStream, mais vous finirez par un octet [] de sorte que votre appel RMI devrait retourne un octet [].

Un petit objet sérialisable ne se compresse pas bien, mais si vous avez plusieurs objets Serializable, il peut très bien se compresser. Donc peut de grandes quantités de texte.

La chose la plus simple à faire est de l'essayer. S'il y a des cordes répétées ou même des portions de cordes, cela aidera la compression.

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(); 
} 

Prints

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

Pourquoi utiliser un intermédiaire ByteArrayOutputStream à tous? –

+1

Pour obtenir le toByteArray(). Existe-t-il un autre moyen d'obtenir l'octet []? –

+0

Voir ma réponse ici, il n'utilise pas ByteArrayOutputStream. Ce n'est vraiment utile que si vous voulez des statistiques. http://stackoverflow.com/a/41550311/1247302 – mrswadge

0

Vous pouvez essayer un custom serialization mechanism pour les éléments à l'intérieur du hashmap.

Quels types d'informations envoyez-vous? à quoi ressemble l'objet à l'intérieur?

Même en utilisant le mécanisme par défaut, et en marquant tous les attributs inutiles comme transitoires aidera.

vous peut en outre tenter d'envoyer les données de votre auto sérialisation avant un ZipOutputStream mais je laisserais que comme une dernière ressource, pour le contenu binaire ne compresse pas trop.

EDIT

Depuis votre utilisant uniquement des chaînes, vous pouvez créer une enveloppe dont la sérialisation personnalisée est un tableau compressé (à peu près comme réponse Peter Lawrey) mais, en utilisant une sérialisation personnalisée vous permettrait d'encapsuler le processus de sérialisation et ont fonctionner d'une façon « transparente » pour RMI (sérialisation RMI ne connaîtrait jamais vous utilisez une version compressée)

Voici une démo:

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(); 
    } 

} 
+0

Ce ne sont que des chaînes à l'intérieur du HashMap ... – jtnire

+0

Essayez d'invoquer "string.intern()" avant de les ajouter à la carte. Espérons que cela réduira la quantité d'objets dans votre carte et facilitera le processus de sérialisation. – OscarRyz

+0

Je me demande comment l'internalisation améliorerait les performances de sérialisation car les chaînes doivent être envoyées à un autre jvm de toute façon –

0

Il y a plusieurs années, j'avais l'habitude de sérialiser des objets dans un tableau d'octets, puis de le compresser. Zip est toujours supporté par Java :) alors essayez cette méthode.