2012-06-23 28 views
10

Tengo un hashmap grande que contiene millones de entradas, y deseo conservarlo en el disco, para que cuando vuelva a leerse en el disco, no tenga la carga adicional de volver a insertar los pares clave-valor en el mapa de nuevo.¿Cómo serializar/deserializar un hashmap?

Estoy tratando de usar la biblioteca de cereales para hacer esto, pero parece que el tipo de datos HashMap debe derivar Genérico. ¿Hay alguna forma de hacer esto?

+0

¿Cuál es el problema con la derivación de genérico? – Cubic

+1

Para derivar genérico, para un tipo personalizado, tenemos que escribir algo como: 'Algo de datos = Algo Int Int derivar Generic' ¿Cómo se puede hacer esto si el tipo de datos se encuentra en una biblioteca en Hackage (aparte de la presentación un parche para el mantenedor de la biblioteca)? – donatello

+0

Hmm ...Bueno, personalmente sospecho que serializar HashMaps como este no va a funcionar, y que terminará teniendo que usar otra implementación que admita el tipo de serialización que desea, pero veamos lo que dicen los demás. – Cubic

Respuesta

0

Actualmente, no hay forma de hacer HashMap serializable sin modificar la biblioteca HashMap.

No es posible hacer que Data.HashMap sea una instancia de Generic (para usar con cereal) usando derivados independientes como se describe en la respuesta de @ mergeconflict, porque Data.HashMap no exporta todos sus constructores (este es un requisito para GHC).

Por lo tanto, la única solución que queda para serializar el HashMap parece ser utilizar la interfaz toList/fromList.

5

Es posible que pueda usar stand-alone deriving para generar su propia instancia Generic para HashMap. Probablemente recibirá una advertencia acerca de orphan instances, pero probablemente tampoco le importe :) De todos modos, no lo he intentado, pero probablemente valga la pena intentarlo ...

+2

Creo que esta es una buena respuesta. Un punto más quizás sea un truco sobre cómo evitar las instancias huérfanas: simplemente defina un nuevo tipo que ajuste un HashMap y defina la instancia para este tipo. Cuando necesite serializar el HashMap, simplemente envuélvalo en su tipo y serialícelo. – Tener

+0

@mergeconflict, @Tener I añadió una línea: 'derivar ejemplo (k Genérico, v Generic) => Genérico (H.HashMap kv)' Pero GHC se queja de que todos los constructores de datos de HashMap no son de alcance (es decir, [no se exportan todos] (http://hackage.haskell.org/packages/archive/unordered-containers/0.2.1.0/doc/html/src/Data-HashMap-Base.html#HashMap)). Parece que este enfoque no funcionará después de todo. donatello

+0

@donatello Bummer :( – mergeconflict

1

No estoy seguro de si usar Generics es una mejor oportunidad para lograr un alto rendimiento. Mi mejor apuesta sería realmente estar escribiendo su propia instancia de Serializable como esto:

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

Para evitar la creación de instancias huérfanas se pueden utilizar newtype truco:

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

La pregunta es cómo definir ...?

No hay una respuesta definitiva antes de intentar implementar y comparar posibles soluciones.

Una posible solución es usar las funciones toList/fromList y almacenar/leer el tamaño de HashMap.

El otro (que sería similar al uso de genéricos) sería escribir serialización directa basada en la estructura interna de HashMap. Dado el hecho de que realmente no tiene las partes internas exportadas, eso sería solo un trabajo para Generics.

0

Si puede utilizar binario, hay binarios huérfanos que proporciona instancias para desordenadas contenedores. No pude instalar binarios huérfanos debido a algún conflicto de cabal, pero solo arrebaté las piezas que necesitaba, por ejemplo:

{-# 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