Round-tripping from LinkedHashMaps to Clojure collections
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
add a comment |
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
Taught me a lot about interop.
– Thumbnail
Sep 13 '16 at 14:59
add a comment |
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
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
memory-management clojure
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
add a comment |
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
add a comment |
1 Answer
1
active
oldest
votes
I think automatically creating keywords from string keys and string keys from keywords is not a good idea, for three reasons.
- It would be surprising to a user to have values change classes when theoretically just pouring the contents of one map into another.
- 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
. - 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.
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
I think automatically creating keywords from string keys and string keys from keywords is not a good idea, for three reasons.
- It would be surprising to a user to have values change classes when theoretically just pouring the contents of one map into another.
- 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
. - 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.
add a comment |
I think automatically creating keywords from string keys and string keys from keywords is not a good idea, for three reasons.
- It would be surprising to a user to have values change classes when theoretically just pouring the contents of one map into another.
- 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
. - 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.
add a comment |
I think automatically creating keywords from string keys and string keys from keywords is not a good idea, for three reasons.
- It would be surprising to a user to have values change classes when theoretically just pouring the contents of one map into another.
- 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
. - 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.
I think automatically creating keywords from string keys and string keys from keywords is not a good idea, for three reasons.
- It would be surprising to a user to have values change classes when theoretically just pouring the contents of one map into another.
- 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
. - 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.
answered 2 hours ago
mtnygardmtnygard
1913
1913
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
Taught me a lot about interop.
– Thumbnail
Sep 13 '16 at 14:59