On Mon, Feb 2, 2009 at 4:48 PM, Keith Bennett <keithrbenn...@gmail.com> wrote:
>
> Clojure definitely has its benefits, but in terms of memory footprint,
> Java appears to be *much* more economical

It's probably worth being careful to separate the different parts of
Java and Clojure.  Clojure code can use most Java data structures
quite comfortably, and Java code can without too much inelegance use
Clojure data structures.  Besides that, each have other benefits and
drawbacks besides the data structures they can use.

> In a Java ArrayList, only a single ArrayList object is used to store
> all n elements added.  The objects are stored in an internal Object
> [].

If you really want a large mutable collection in your Clojure code,
you can use an ArrayList quite easily:

(def a1 (java.util.ArrayList.))
(dotimes [_ 100000] (.add a1 "x"))

user=> (take 10 a1)
("x" "x" "x" "x" "x" "x" "x" "x" "x" "x")

Here Clojure is adding essentially no memory overhead at all, beyond
what the ArrayList itself brings, and the resulting object plays quite
well with many common Clojure idioms.

But mutability has it's own costs, and Clojure provides several
options to increase your chances of finding an immutable solution that
also fits in your memory and CPU performance requirements.

For example, chances are you'll want to be accessing a large ordered
collection like this by index.  In this case the PersistentList you
used in your original example is the wrong choice anyway, since it's
O(n) for lookups.  Instead, you might like one of the vector types:

(def v1 (vec (replicate 100000 "x")))

Take a look at that with your profiling tool and you'll see that it's
taking very little memory.  In fact, the data is stored in a single
Java array Object[].

user=> (class v1)
clojure.lang.LazilyPersistentVector

But unlike ArrayList, this is immutable, so you can get a new object
with one element changed:

(def v2 (assoc v1 5 :foo))

user=> (take 10 v2)
("x" "x" "x" "x" "x" :foo "x" "x" "x" "x")

When you do this, you'll see your memory jump a bit, as you now have
both the original LazilyPersistentVector and the new PersistentVector:

user=> (class v2)
clojure.lang.PersistentVector

PersistentVectors have structural sharing, so each subsequent "copy"
you make with elements changed will cost much less memory then new
full copies of the whole vector would.

I feel like I'm rambling now, so let me tie this off.  Depending on
your actual use case, vectors may work well.  If you don't need the
whole collection at once, perhaps a lazy seq that simply promises the
values as they're demanded would work.  One of the Map types might be
good if your collection is likely to be sparse.  Or if none of these
immutable options will do, there are always ArrayList or even raw Java
arrays at hand.

You don't need to give up macros and the REPL just because a
100000-element cons-list feels bloated. :-)

--Chouser

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to