Round-tripping from LinkedHashMaps to Clojure collections












3














I'm trying to implement round-tripping from LinkedHashMaps to Clojure collections. The implementation below works for smaller collections, but larger collections raise OutOfMemoryError: GC overhead limit exceeded. Below is a generative test that passes, when the number of tests is small (~10), but exhausts memory for larger numbers of tests (~100).



I would appreciate any tips how to reduce the memory requirements of the implementation as well as general tips on how to improve the code's quality.



(import '[clojure.lang IPersistentMap IPersistentVector]
'[java.util ArrayList LinkedHashMap])

(defprotocol LinkedHashMappable
"Convert Clojure data structure to LinkedHashMap."
(->linked-hash-map [data]))

(extend-protocol LinkedHashMappable
IPersistentMap
(->linked-hash-map [m]
(let [lhm (LinkedHashMap.)]
(doseq [[k v] m]
(.put lhm
(if (keyword? k) (name k) k)
(->linked-hash-map v)))
lhm))

IPersistentVector
(->linked-hash-map [v]
(let [alist (ArrayList.)]
(doseq [i v] (.add alist (->linked-hash-map i)))
alist))

Object
(->linked-hash-map [o] o))

(defprotocol Clojurizable
"Convert LinkedHashMap to Clojure data structure."
(->clj [lhm]))

(extend-protocol Clojurizable
LinkedHashMap
(->clj [lhm]
(reduce (fn [m [k v]]
(assoc m (keyword k) (->clj v)))
{}
(iterator-seq (.. lhm entrySet iterator))))

ArrayList
(->clj [alist]
(mapv ->clj alist))

Object
(->clj [o] o))

; ----- Test -----

(require '[clojure.test.check :as tc]
'[clojure.test.check.generators :as gen]
'[clojure.test.check.properties :as prop])

(def map-generator
(gen/recursive-gen (fn [inner]
(gen/map gen/keyword (gen/one-of [(gen/map gen/keyword inner)
(gen/vector inner)])))
gen/string))

(def linked-hash-map-round-tripping
(prop/for-all [m map-generator]
(= (->clj (->linked-hash-map m)) m)))

; The test passes for smaller numbers of tests (~10), but exhausts memory for larger numbers (~100).
#_(tc/quick-check 100 linked-hash-map-round-tripping)









share|improve this question






















  • Taught me a lot about interop.
    – Thumbnail
    Sep 13 '16 at 14:59
















3














I'm trying to implement round-tripping from LinkedHashMaps to Clojure collections. The implementation below works for smaller collections, but larger collections raise OutOfMemoryError: GC overhead limit exceeded. Below is a generative test that passes, when the number of tests is small (~10), but exhausts memory for larger numbers of tests (~100).



I would appreciate any tips how to reduce the memory requirements of the implementation as well as general tips on how to improve the code's quality.



(import '[clojure.lang IPersistentMap IPersistentVector]
'[java.util ArrayList LinkedHashMap])

(defprotocol LinkedHashMappable
"Convert Clojure data structure to LinkedHashMap."
(->linked-hash-map [data]))

(extend-protocol LinkedHashMappable
IPersistentMap
(->linked-hash-map [m]
(let [lhm (LinkedHashMap.)]
(doseq [[k v] m]
(.put lhm
(if (keyword? k) (name k) k)
(->linked-hash-map v)))
lhm))

IPersistentVector
(->linked-hash-map [v]
(let [alist (ArrayList.)]
(doseq [i v] (.add alist (->linked-hash-map i)))
alist))

Object
(->linked-hash-map [o] o))

(defprotocol Clojurizable
"Convert LinkedHashMap to Clojure data structure."
(->clj [lhm]))

(extend-protocol Clojurizable
LinkedHashMap
(->clj [lhm]
(reduce (fn [m [k v]]
(assoc m (keyword k) (->clj v)))
{}
(iterator-seq (.. lhm entrySet iterator))))

ArrayList
(->clj [alist]
(mapv ->clj alist))

Object
(->clj [o] o))

; ----- Test -----

(require '[clojure.test.check :as tc]
'[clojure.test.check.generators :as gen]
'[clojure.test.check.properties :as prop])

(def map-generator
(gen/recursive-gen (fn [inner]
(gen/map gen/keyword (gen/one-of [(gen/map gen/keyword inner)
(gen/vector inner)])))
gen/string))

(def linked-hash-map-round-tripping
(prop/for-all [m map-generator]
(= (->clj (->linked-hash-map m)) m)))

; The test passes for smaller numbers of tests (~10), but exhausts memory for larger numbers (~100).
#_(tc/quick-check 100 linked-hash-map-round-tripping)









share|improve this question






















  • Taught me a lot about interop.
    – Thumbnail
    Sep 13 '16 at 14:59














3












3








3







I'm trying to implement round-tripping from LinkedHashMaps to Clojure collections. The implementation below works for smaller collections, but larger collections raise OutOfMemoryError: GC overhead limit exceeded. Below is a generative test that passes, when the number of tests is small (~10), but exhausts memory for larger numbers of tests (~100).



I would appreciate any tips how to reduce the memory requirements of the implementation as well as general tips on how to improve the code's quality.



(import '[clojure.lang IPersistentMap IPersistentVector]
'[java.util ArrayList LinkedHashMap])

(defprotocol LinkedHashMappable
"Convert Clojure data structure to LinkedHashMap."
(->linked-hash-map [data]))

(extend-protocol LinkedHashMappable
IPersistentMap
(->linked-hash-map [m]
(let [lhm (LinkedHashMap.)]
(doseq [[k v] m]
(.put lhm
(if (keyword? k) (name k) k)
(->linked-hash-map v)))
lhm))

IPersistentVector
(->linked-hash-map [v]
(let [alist (ArrayList.)]
(doseq [i v] (.add alist (->linked-hash-map i)))
alist))

Object
(->linked-hash-map [o] o))

(defprotocol Clojurizable
"Convert LinkedHashMap to Clojure data structure."
(->clj [lhm]))

(extend-protocol Clojurizable
LinkedHashMap
(->clj [lhm]
(reduce (fn [m [k v]]
(assoc m (keyword k) (->clj v)))
{}
(iterator-seq (.. lhm entrySet iterator))))

ArrayList
(->clj [alist]
(mapv ->clj alist))

Object
(->clj [o] o))

; ----- Test -----

(require '[clojure.test.check :as tc]
'[clojure.test.check.generators :as gen]
'[clojure.test.check.properties :as prop])

(def map-generator
(gen/recursive-gen (fn [inner]
(gen/map gen/keyword (gen/one-of [(gen/map gen/keyword inner)
(gen/vector inner)])))
gen/string))

(def linked-hash-map-round-tripping
(prop/for-all [m map-generator]
(= (->clj (->linked-hash-map m)) m)))

; The test passes for smaller numbers of tests (~10), but exhausts memory for larger numbers (~100).
#_(tc/quick-check 100 linked-hash-map-round-tripping)









share|improve this question













I'm trying to implement round-tripping from LinkedHashMaps to Clojure collections. The implementation below works for smaller collections, but larger collections raise OutOfMemoryError: GC overhead limit exceeded. Below is a generative test that passes, when the number of tests is small (~10), but exhausts memory for larger numbers of tests (~100).



I would appreciate any tips how to reduce the memory requirements of the implementation as well as general tips on how to improve the code's quality.



(import '[clojure.lang IPersistentMap IPersistentVector]
'[java.util ArrayList LinkedHashMap])

(defprotocol LinkedHashMappable
"Convert Clojure data structure to LinkedHashMap."
(->linked-hash-map [data]))

(extend-protocol LinkedHashMappable
IPersistentMap
(->linked-hash-map [m]
(let [lhm (LinkedHashMap.)]
(doseq [[k v] m]
(.put lhm
(if (keyword? k) (name k) k)
(->linked-hash-map v)))
lhm))

IPersistentVector
(->linked-hash-map [v]
(let [alist (ArrayList.)]
(doseq [i v] (.add alist (->linked-hash-map i)))
alist))

Object
(->linked-hash-map [o] o))

(defprotocol Clojurizable
"Convert LinkedHashMap to Clojure data structure."
(->clj [lhm]))

(extend-protocol Clojurizable
LinkedHashMap
(->clj [lhm]
(reduce (fn [m [k v]]
(assoc m (keyword k) (->clj v)))
{}
(iterator-seq (.. lhm entrySet iterator))))

ArrayList
(->clj [alist]
(mapv ->clj alist))

Object
(->clj [o] o))

; ----- Test -----

(require '[clojure.test.check :as tc]
'[clojure.test.check.generators :as gen]
'[clojure.test.check.properties :as prop])

(def map-generator
(gen/recursive-gen (fn [inner]
(gen/map gen/keyword (gen/one-of [(gen/map gen/keyword inner)
(gen/vector inner)])))
gen/string))

(def linked-hash-map-round-tripping
(prop/for-all [m map-generator]
(= (->clj (->linked-hash-map m)) m)))

; The test passes for smaller numbers of tests (~10), but exhausts memory for larger numbers (~100).
#_(tc/quick-check 100 linked-hash-map-round-tripping)






memory-management clojure






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Aug 11 '16 at 8:21









Jindřich MynarzJindřich Mynarz

1162




1162












  • Taught me a lot about interop.
    – Thumbnail
    Sep 13 '16 at 14:59


















  • Taught me a lot about interop.
    – Thumbnail
    Sep 13 '16 at 14:59
















Taught me a lot about interop.
– Thumbnail
Sep 13 '16 at 14:59




Taught me a lot about interop.
– Thumbnail
Sep 13 '16 at 14:59










1 Answer
1






active

oldest

votes


















0














I think automatically creating keywords from string keys and string keys from keywords is not a good idea, for three reasons.




  1. It would be surprising to a user to have values change classes when theoretically just pouring the contents of one map into another.

  2. In the space of possible key transformations, keyword<-->string is just one point. As a user, I might want to do many other kinds of transformations while converting. For a more general approach, think about how transducers can be supplied to the four-argument form of into.

  3. Keywords are interned. This may be a source of your memory trouble. Every new keyword gets stored permanently in the heap. That's how Clojure does fast keyword comparison by just checking object references. That means a property-based test that generates huge numbers of unique keywords is going to consume a lot of memory.






share|improve this answer





















    Your Answer





    StackExchange.ifUsing("editor", function () {
    return StackExchange.using("mathjaxEditing", function () {
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    });
    });
    }, "mathjax-editing");

    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "196"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f138417%2fround-tripping-from-linkedhashmaps-to-clojure-collections%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    I think automatically creating keywords from string keys and string keys from keywords is not a good idea, for three reasons.




    1. It would be surprising to a user to have values change classes when theoretically just pouring the contents of one map into another.

    2. In the space of possible key transformations, keyword<-->string is just one point. As a user, I might want to do many other kinds of transformations while converting. For a more general approach, think about how transducers can be supplied to the four-argument form of into.

    3. Keywords are interned. This may be a source of your memory trouble. Every new keyword gets stored permanently in the heap. That's how Clojure does fast keyword comparison by just checking object references. That means a property-based test that generates huge numbers of unique keywords is going to consume a lot of memory.






    share|improve this answer


























      0














      I think automatically creating keywords from string keys and string keys from keywords is not a good idea, for three reasons.




      1. It would be surprising to a user to have values change classes when theoretically just pouring the contents of one map into another.

      2. In the space of possible key transformations, keyword<-->string is just one point. As a user, I might want to do many other kinds of transformations while converting. For a more general approach, think about how transducers can be supplied to the four-argument form of into.

      3. Keywords are interned. This may be a source of your memory trouble. Every new keyword gets stored permanently in the heap. That's how Clojure does fast keyword comparison by just checking object references. That means a property-based test that generates huge numbers of unique keywords is going to consume a lot of memory.






      share|improve this answer
























        0












        0








        0






        I think automatically creating keywords from string keys and string keys from keywords is not a good idea, for three reasons.




        1. It would be surprising to a user to have values change classes when theoretically just pouring the contents of one map into another.

        2. In the space of possible key transformations, keyword<-->string is just one point. As a user, I might want to do many other kinds of transformations while converting. For a more general approach, think about how transducers can be supplied to the four-argument form of into.

        3. Keywords are interned. This may be a source of your memory trouble. Every new keyword gets stored permanently in the heap. That's how Clojure does fast keyword comparison by just checking object references. That means a property-based test that generates huge numbers of unique keywords is going to consume a lot of memory.






        share|improve this answer












        I think automatically creating keywords from string keys and string keys from keywords is not a good idea, for three reasons.




        1. It would be surprising to a user to have values change classes when theoretically just pouring the contents of one map into another.

        2. In the space of possible key transformations, keyword<-->string is just one point. As a user, I might want to do many other kinds of transformations while converting. For a more general approach, think about how transducers can be supplied to the four-argument form of into.

        3. Keywords are interned. This may be a source of your memory trouble. Every new keyword gets stored permanently in the heap. That's how Clojure does fast keyword comparison by just checking object references. That means a property-based test that generates huge numbers of unique keywords is going to consume a lot of memory.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered 2 hours ago









        mtnygardmtnygard

        1913




        1913






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Code Review Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            Use MathJax to format equations. MathJax reference.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f138417%2fround-tripping-from-linkedhashmaps-to-clojure-collections%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            404 Error Contact Form 7 ajax form submitting

            How to know if a Active Directory user can login interactively

            Refactoring coordinates for Minecraft Pi buildings written in Python