2012-06-23 24 views
10

Ho una grande hashmap contenente milioni di voci e voglio mantenerle su disco, così che quando viene letta di nuovo dal disco, non ho il sovraccarico di inserire nuovamente le coppie valore-chiave nella mappa ancora.Come serializzare/deserializzare una hashmap?

Sto cercando di utilizzare la libreria di cereali per eseguire questa operazione, ma sembra che il tipo di dati HashMap debba derivare generico. C'è un modo per fare questo?

+0

Qual è il problema con il derivato generico? – Cubic

+1

Per ricavare generico, per un tipo personalizzato, abbiamo bisogno di scrivere qualcosa di simile: 'Qualcosa di dati = Qualcosa Int Int derivante Generic' Come può essere fatto se il tipo di dati è in una libreria su Hackage (diverso da quello della presentazione una patch per il manutentore della libreria)? – donatello

+0

Hmm ...Beh, personalmente sospetto che Serializing HashMaps come questo non funzioni, e che finirai per dover usare un'altra implementazione che supporti il ​​tipo di serializzazione che vuoi, ma vediamo cosa dicono gli altri. – Cubic

risposta

0

Attualmente, non è possibile rendere serializzabile HashMap senza modificare la libreria HashMap stessa.

Non è possibile rendere Data.HashMap un'istanza di Generic (per l'uso con cereali) utilizzando la derivazione autonoma come descritto dalla risposta di @ mergeconflict, poiché Data.HashMap non esporta tutti i suoi costruttori (questo è un requisito per GHC).

Quindi, l'unica soluzione rimasta per serializzare HashMap sembra essere quella di utilizzare l'interfaccia toList/fromList.

5

Potrebbe essere possibile utilizzare stand-alone deriving per generare la propria istanza Generic per HashMap. Probabilmente riceverai un avvertimento su orphan instances, ma probabilmente non ti interessa :) Comunque, non ho provato questo, ma probabilmente vale la pena sparare ...

+2

Penso che questa sia una buona risposta. Un altro punto è forse un trucco su come evitare le istanze orfane: basta definire un nuovo tipo che avvolge una HashMap e definisce l'istanza per questo tipo. Quando hai bisogno di serializzare la HashMap, semplicemente avvolgilo nel tuo tipo e serializza. – Tener

+0

@mergeconflict, @Tener Ho aggiunto una riga: 'istanza derivante (Generic k, Generic v) => Generico (H.HashMap kv)' Ma GHC si lamenta che tutti i costruttori di dati di HashMap non sono nell'ambito (cioè [non sono tutti esportati] (http://hackage.haskell.org/packages/archive/unordered-containers/0.2.1.0/doc/html/src/Data-HashMap-Base.html#HashMap)). Sembra che questo approccio non funzionerà dopo tutto. donatello

+0

@donatello Bummer :( – mergeconflict

1

Non sono sicuro che l'uso di Generics è un colpo migliore per raggiungere alte prestazioni. La mia soluzione migliore sarebbe effettivamente scrivere il proprio istanza per Serializable come questo:

instance (Serializable a) => Serializable (HashMap a) where 
    ... 

per evitare di creare istanze di orfani è possibile utilizzare Newtype trucco:

newtype SerializableHashMap a = SerializableHashMap { toHashMap :: HashMap a } 
instance (Serializable a) => SerializableHashMap a where 
    ... 

La domanda è come definire ...?

Non esiste una risposta definitiva prima di provare e implementare e confrontare le possibili soluzioni.

Una possibile soluzione è utilizzare le funzioni toList/fromList e memorizzare/leggere la dimensione dello HashMap.

L'altro (che sarebbe simile all'utilizzo di Generics) sarebbe scrivere la serializzazione diretta in base alla struttura interna di HashMap. Dato che non hai davvero esportato i componenti interni, sarebbe un lavoro solo per Generics.

0

Se è possibile utilizzare binari, c'è binari orfani che fornisce istanze per non ordinate contenitori. Non ho potuto installare gli orfani binari a causa di alcuni conflitti di cabala, ma ho semplicemente prelevato le parti necessarie, ad esempio

{-# LANGUAGE CPP   #-} 
{-# LANGUAGE DeriveGeneriC#-} 

module Bin where 

import   Data.Binary 
import   Data.ByteString.Lazy.Internal 
import   Data.Hashable     (Hashable) 
import qualified Data.HashMap.Strict   as M 
import qualified Data.Text      as T 

#if !(MIN_VERSION_text(1,2,1)) 
import   Data.Text.Binary    () 
#endif 

instance (Hashable k, Eq k, Binary k, Binary v) => Binary (M.HashMap k v) where 
    get = fmap M.fromList get 
    put = put . M.toList 

-- Note: plain `encode M.fromList []` without type annotations won't work 
encodeModel :: M.HashMap T.Text Int -> ByteString 
encodeModel m = 
    encode m