This guide covers:

  • Collections in Clojure
  • Sequences in Clojure
  • Core collection types
  • Key operations on collections and sequences
  • Other topics related to collections and sequences

This work is licensed under a Creative Commons Attribution 3.0 Unported License (including images & stylesheets). The source is available on Github.

What Version of Clojure Does This Guide Cover?

This guide covers Clojure 1.5.

Overview

Clojure provides a number of powerful abstractions including collections and sequences. When working with Clojure, many operations are expressed as a series of operations on collections or sequences.

Most of Clojure's core library treats collections and sequences the same way, although sometimes a distinction has to be made (e.g. with lazy infinite sequences).

clojure.core provides many fundamental operations on collections, such as: map, filter, remove, take, and drop. Basic operations on collections and sequences are combined to implement more complex operations.

Clojure Collections are Immutable (Persistent)

Clojure collections are immutable (persistent). The term "persistent data structures" has nothing to do with durably storing them on disk. What it means is that collections are mutated (updated) by producing new collections. To quote Wikipedia:

In computing, a persistent data structure is a data structure that always preserves the previous version of itself when it is modified. Such data structures are effectively immutable, as their operations do not (visibly) update the structure in-place, but instead always yield a new updated structure.

Clojure's persistent data structures are implemented as trees and tries and have O(log32 n) access complexity where n is the number of elements.

The Collection Abstraction

Clojure has a collection abstraction with several key operations supported for all collection implementations. They are

  • =: checks value equality of a collection compared to other collections
  • count: returns number of elements in a collection
  • conj: adds an item to a collection in the most efficient way
  • empty: returns an empty collection of the same type as the argument
  • seq: gets a sequence of a collection

These functions work on all core Clojure collection types.

Core Collection Types

Clojure has several core collection types:

  • Maps (called hashes or dictionaries in some other languages)
  • Vectors
  • Lists
  • Sets

Maps

Maps associate keys with values. Boths keys and values can be of any type, but keys must be comparable. There are several implementations of maps with different guarantees about ordering. Hash maps are typically instantiated with literals:

{:language "Clojure" :creator "Rich Hickey"}

Commas can be used in map literals (Clojure compiler treats the comma as whitespace):

{:language "Clojure", :creator "Rich Hickey"}

clojure.core/sorted-map and clojure.core/array-map produce ordered maps:

(sorted-map :language "Clojure" :creator "Rich Hickey")
;; ⇒ {:creator "Rich Hickey", :language "Clojure"}
(array-map :language "Clojure" :creator "Rich Hickey")
;; ⇒ {:creator "Rich Hickey", :language "Clojure"}

Unsurprisingly, map literals must contain an even number of forms (as many keys as values). Otherwise the code will not compile:

{:language "Clojure" :creator}

In general, the only major difference between Clojure maps and maps/hashes/dictionaries in some other languages is that Clojure maps are immutable. When a Clojure map is modified, the result is a new map that internally has structural sharing (for efficiency reasons) but semantically is a separate immutable value.

Maps As Functions

Maps in Clojure can be used as functions on their keys. See the Functions guide for more information.

Keywords As Functions

Keywords in Clojure can be used as functions on maps. See the Functions guide for more information.

Vectors

Vectors are collections that offer efficient random access (by index). They are typically instantiated with literals:

[1 2 3 4]

["clojure" "scala" "erlang" "f#" "haskell" "ocaml"]

Commas can be used to separate vector elements (Clojure compiler treats the comma as whitespace):

["clojure", "scala", "erlang", "f#", "haskell", "ocaml"]

Unlike lists, vectors are not used for function invocation. They are, however, used to make certain forms (e.g. the list of locals in let or parameters in defn) stand out visually. This was an intentional decision in Clojure design.

Lists

Lists in Clojure are singly linked lists. Access or modifications of list head is efficient, random access is not.

Lists in Clojure are special because they represent code forms, from function calls to macro calls to special forms. Code is data in Clojure and it is represented primarily as lists:

(empty? [])

First item on the list is said to be in the calling position.

When used as "just" data structures, lists are typically instantiated with literals with quoting:

'(1 2 3 4)
'("clojure" "scala" "erlang" "f#" "haskell" "ocaml")

Or you can explicitly use the list form:

(list 1 2 3 4)
;; ⇒ (1 2 3 4)

Commas can be used to separate list elements (Clojure compiler treats the comma as whitespace):

'("clojure", "scala", "erlang", "f#", "haskell", "ocaml")

Lists and Metaprogramming in Clojure

Metaprogramming in Clojure (and other Lisp dialects) is different from metaprogramming in, say, Ruby, because in Ruby metaprogramming is primarily about producing strings while in Clojure it is about producing data structures (mostly lists). For sophisticated DSLs, producing data structures directly lets developers avoid a lot of incidental complexity that string generation brings along.

This topic is covered in detail in the Macros and Metaprogramming.

Sets

Sets are collections that offer efficient membership check operation and only allow each element to appear in the collection once. They are typically instantiated with literals:

#{1 2 3 4}
#{"clojure" "scala" "erlang" "f#" "haskell" "ocaml"}

Commas can be used to separate set elements (Clojure compiler treats the as whitespace):

#{"clojure", "scala", "erlang", "f#", "haskell", "ocaml"}

Sets As Functions

Sets in Clojure can be used as functions on their elements. See the Functions guide for more information.

Set Membership Checks

The most common way of checking if an element is in a set is by using set as a function:

(#{1 2 3 4} 1)
;; ⇒ 1
(#{1 2 3 4} 10)
;; ⇒ nil
(if (#{1 2 3 4} 1)
  :hit
  :miss)
;; ⇒ :hit

Sequences

The sequence abstraction represents a sequential view of a collection or collection-like entity (computation result).

clojure.core/seq is a function that produces a sequence over the given argument. Data types that clojure.core/seq can produce a sequence over are called seqable:

  • Clojure collections
  • Java maps
  • All iterable types (types that implement java.util.Iterable)
  • Java collections (java.util.Set, java.util.List, etc)
  • Java arrays
  • All types that implement java.lang.CharSequence interface, including Java strings
  • All types that implement clojure.lang.Seqable interface
  • nil

The sequence abstraction supports several operations:

  • first
  • rest
  • next

and there are two ways to produce a sequence:

  • seq produces a sequence over its argument (often a collection)
  • lazy-seq creates a lazy sequence (that is produced by performing computation)

seq, cons, list*

clojure.core/seq takes a single argument and returns a sequential view over it:

(seq [1 2 3])
;; ⇒ (1 2 3)

When given an empty collection or sequence, clojure.core/seq returns nil:

(seq [])
;; ⇒ nil

this is commonly used in the following pattern:

(if (seq xs)
  (comment "Do something with this sequence")
  (comment "Do something else"))

Another function that constructs sequences is clojure.core/cons. It prepends values to the head of the given sequence:

(cons 0 (range 1 3))
;; ⇒ (0 1 2)

clojure.core/list* does the same for a number of values:

(list* 0 1 (range 2 5))
;; ⇒ (0 1 2 3 4)

clojure.core/cons and clojure.core/list* are primarily used to produce lazy sequences and in metaprogramming (when writing macros). As far as metaprogramming goes, sequences and lists are the same and it is common to add items in the beginning of the list (into the calling position).

Note that clojure.core/cons does not create cons cells and lists in Clojure are not implemented as linked cons cells (like in many other dialects of Lisp).

first, rest, next

clojure.core/first returns the first item in the sequence. clojure.core/next and clojure.core/rest return the rest:

(first (seq [1 2 3 4 5 6]))
;; ⇒ 1

(rest (seq [1 2 3 4 5 6]))
;; ⇒ (2 3 4 5 6)

the difference between them is what they return on a single element sequence:

(rest (seq [:one]))
;; ⇒ ()
(next (seq [:one]))
;; ⇒ nil

Lazy Sequences in Clojure

Lazy sequences are produced by performing computation or I/O. They can be infinite or not have exact length (e.g. a sequence of all powers of 2 or an audio stream).

Lazy sequences is an broad topic and covered in the Laziness guide.

Key Operations on Collections and Sequences

Below is an overview of clojure.core functions that work on collections and sequences. Most of them work the same way for all types of collections, however, there are exception to this rule. For example, functions like clojure.core/assoc, clojure.core/dissoc and clojure.core/get-in only really make sense in the context of maps and other associative data structures (for example, records).

clojure.core/conj adds elements to a collection in the most efficient manner, which depends on collection implementation details and won't be the same for vectors and lists.

In general, Clojure design emphasizes that operations on collections and sequences should be uniform and follow the principle of least surprise. In real world projects, however, the difference between algorithmic complexity and other runtime characteristics of various collection types often cannot be ignored. Keep this in mind.

You can find more information in the clojure.core Overview and Clojure cheatsheet.

count

Returns a count of the number of items in a collection. An argument of nil returns 0.

(count "Hello")
;; ⇒ 5
(count [1 2 3 4 5 6 7])
;; ⇒ 7
(count nil)
;; ⇒ 0

Note that count does not return in constant time for all collections. This can be determined with counted?. Keep in mind that lazy sequences must be realized to get a count of the items. This is often not intended and can cause a variety of otherwise cryptic errors.

(counted? "Hello")
;; ⇒ false
;; will be fully realized when using (count (range 10))
(counted? (range 10))
;; ⇒ false
;; Constant time return of (count)
(counted? [1 2 3 4 5])
;; ⇒ true

conj

conj is short for "conjoin". As the name implies, conj takes a collection and argument(s) and returns the collection with those arguments added.

Adding items to a collection occurs at different places depending on the concrete type of collection.

List addition occurs at the beginning of the list. This is because accessing the head of the list is a constant time operation, and accessing the tail requires traversal of the entire list.

(conj '(1 2) 3)
;; ⇒ (3 1 2)

Vectors have constant time access across the entire data structure. `'conj' thusly appends to the end of a vector.

(conj [1 2] 3)
;; ⇒ [1 2 3]

Maps do not have guaranteed ordering, so the location that items are added is irrelevant. conj requires vectors of [key value] pairs to be added to the map.

(conj {:a 1 :b 2 :c 3} [:d 4])
;; ⇒ {:d 4, :a 1, :c 3, :b 2}
(conj {:cats 1 :dogs 2} [:ants 400] [:giraffes 13])
;; ⇒ {:giraffes 13, :ants 400, :cats 1, :dogs 2}

Sets also do not have guaranteed ordering. conj returns a set with the item added. As the concept of sets implies, added items will not duplicate equivalent items if they are present in the set.

(conj #{1 4} 5)
;; ⇒ #{1 4 5}
(conj #{:a :b :c} :b :c :d :e)
;; ⇒ #{:a :c :b :d :e}

get

get returns the value for the specified key in a map or record, index of a vector or value in a set. If the key is not present, get returns nil or a supplied default value.

;; val of a key in a map
(get {:a 1 :b 2 :c 3} :b)
;; ⇒ 2
;; index of a vector
(get [10 15 20 25] 2)
;; ⇒ 20
;; in a set, returns the value itself if present
(get #{1 10 100 2 20 200} 1)
;; ⇒ 1

```klipse-clojure
;; returns nil if key is not present
(get {:a 1 :b 2} :c)
;; ⇒ nil
;; vector does not have an _index_ of 4. nil is returned
(get [1 2 3 4] 4)
;; ⇒ nil
(defrecord Hand [index middle ring pinky thumb])
(get (Hand. 3 4 3.5 2 2) :index)
;; ⇒ 3

get also supports a default return value supplied as the last argument.

;; index 4 does not exist. return default value
(get [1 2 3 4] 4 "Not Found")
;; ⇒ "Not Found"
;; key :c does not exist, so return default value of 3
(get {:a 1 :b 2} :c 3)
;; ⇒ 3

assoc

assoc takes a key and a value and returns a collection of the same type as the supplied collection with the key mapped to the new value.

assoc is similar to get in how it works with maps, records or vectors. When applied to a map or record, the same type is returned with the key/value pairs added or modified. When applied to a vector, a vector is returned with the key acting as an index and the index being replaced by the value.

Since maps and records can not contain multiple equivalent keys, supplying assoc with a key/value that exists in the one will cause assoc to return modify the key at that value in the result and not duplicate the key.

(assoc {:a 1} :b 2)
;; ⇒ {:b 2, :a 1}
(assoc {:a 1 :b 45 :c 3} :b 2)
;; ⇒ {:a 1, :c 3, :b 2}
(defrecord Hand [index middle ring pinky thumb])
(assoc (Hand. 3 4 3.5 2 2) :index 3.75)
;; ⇒ #user.Hand{:index 3.75, :middle 4, :ring 3.5, :pinky 2, :thumb 2}

When using assoc with a vector, the key is the index and the value is the value to assign to that index in the returned vector. The key must be <= (count vector) or an index out of bounds error will occur.

(assoc [1 2 76] 2 3) ; ⇒ [1 2 3]
;; index 5 does not exist. valid indexes for this vector are: 0, 1, 2
(assoc [1 2 3] 5 6)
;; the error here is slightly different in Clojure/Script

When the key is equal to (count vector) assoc will add an item to the vector.

(assoc [1 2 3] 3 4) ; ⇒ [1 2 3 4]

dissoc

dissoc returns a map with the supplied keys, and subsequently their values, removed. Unlike assoc, dissoc does not work on vectors. When a record is provided, dissoc returns a map. For similar functionality with vectors, see subvec and concat.

(dissoc {:a 1 :b 2 :c 3} :b)
;; ⇒ {:a 1, :c 3}
(dissoc {:a 1 :b 14 :c 390 :d 75 :e 2 :f 51} :b :c :e)
;; ⇒ {:a 1, :f 51, :d 75}
;; note that a map is returned, not a record.
(defrecord Hand [index middle ring pinky thumb])
;; always be careful with the bandsaw!
(dissoc (Hand. 3 4 3.5 2 2) :ring)
;; ⇒ {:index 3, :middle 4, :pinky 2, :thumb 2}

first

first returns the first item in the collection. first returns nil if the argument is empty or is nil.

Note that for collections that do not guarantee order like some maps and sets, the behaviour of first should not be relied on.

(first (range 10))
;; ⇒ 0
(first [:floor :piano :seagull])
;; ⇒ :floor
(first [])
;; ⇒ nil

rest

rest returns a seq of items starting with the second element in the collection. rest returns an empty seq if the collection only contains a single item.

rest should also not be relied on when using maps and sets unless you are sure ordering is guaranteed.

(rest [13 1 16 -4])
;; ⇒ (1 16 -4)
(rest '(:french-fry))
;; ⇒ '()

The behaviour of rest should be contrasted with next. next returns nil if the collection only has a single item. This is important when considering "truthiness" of values since an empty seq is "true" but nil is not.

(if (rest '("stuff"))
  (println "Does this print?"))
;; yes, it prints.
;; NEVER FINISHES EXECUTION!!!
;; "done" is never reached because (rest x) is always a "true" value
(defn inf
  [x]
  (if (rest x)
    (inf (rest x))
    "done"))

empty?

empty? returns true if the collection has no items, or false if it has 1 or more items.

(empty? [])
;; ⇒ true
(empty? '(1 2 3))
;; ⇒ false

Do not confuse empty? with empty. This can be a source of great confusion:

(if (empty [1 2 3]) ;; empty returns an empty seq, which is true! use empty? here.
  "It's empty"
  "It's not empty")
;; ⇒ "It's empty"

empty

empty returns an empty collection of the same type as the collection provided.

(empty [1 2 3])
;; ⇒ []
(empty {:a 1 :b 2 :c 3})
;; ⇒ {}

not-empty

not-empty returns nil if the collection has no items. If the collection contains items, the collection is returned.

(not-empty '(:mice :elephants :children))
;; ⇒ (:mice :elephants :children)
(not-empty '())
;; ⇒ nil

contains?

contains returns true if the provided key is present in a collection. contains is similar to get in that vectors treat the key as an index. contains will always return false for lists.

(contains? {:a 1 :b 2 :c 3} :c)
;; ⇒ true
;; true if index 2 exists
(contains? ["John" "Mary" "Paul"] 2)
;; ⇒ true
;; false if index 5 does not exist
(contains? ["John" "Mary" "Paul"] 5)
;; ⇒ false
;; "Paul" does not exist as an index
(contains? ["John" "Mary" "Paul"] "Paul")
;; ⇒ false
;; lists are not supported. contains? won't traverse a collection for a result.
(contains? '(1 2 3) 0)
;; ⇒ java.lang.IllegalArgumentException: contains? not supported on type: clojure.lang.PersistentList

some

some will apply a predicate to each value in a collection until a non-false/nil result is returned then immediately return that result.

Since collections are "true" values, this makes it possible to return the first result itself rather than simply true.

(some even? [1 2 3 4 5])
;; ⇒ true
;; predicate returns the value rather than simply true
(some #(if (even? %) %) [1 2 3 4 5])
;; ⇒ 2

Since maps can be used as functions, you can use a map as a predicate. This will return the value of the first key in the collection that is also in the map.

(some {:a 1 :b 5} [:h :k :d :b])
;; ⇒ 5

Sets can also be used as functions and will return the first item in the collection that is present in the set.

(some #{4} (range 20))
;; ⇒ 4

every?

every returns true if the predicate returns true for every item in the collection, otherwise it returns false.

(every? even? (range 0 10 2))
;; ⇒ true
;; set can be used to see if collection only contains items in the set.
(every? #{2 3 4} [2 3 4 2 3 4])
;; ⇒ true

map

map is used to sequence of values and generate a new sequence of values.

Essentially, you're creating a mapping from an old sequence of values to a new sequence of values.

(def numbers
  (range 1 10))
;; ⇒ (1 2 3 4 5 6 7 8 9)
(map (partial * 2) numbers)
;; ⇒ (2 4 6 8 10 12 14 16 18)
(def scores
  {:clojure 10
   :scala 9
   :jruby 8})

(map #(str "Team " (name (key %)) " has scored " (val %)) scores)
;; ⇒ ("Team scala has scored 9" "Team jruby has scored 8" "Team clojure has scored 10")

reduce

reduce takes a sequence of values and a function. It applies that function repeatedly with the sequence of values to reduce it to a single value.

(def numbers
  (range 1 10))
;; ⇒ (1 2 3 4 5 6 7 8 9)
(reduce + numbers)
;; ⇒ 45
(def scores
  {:clojure 10
   :scala 9
   :jruby 8})

(reduce + (vals scores))
;; ⇒ 27
;; Provide an initial value for the calculation
(reduce + 10 (vals scores))
;; ⇒ 37

filter

filter returns a lazy sequence of items that return true for the provided predicate. Contrast to remove.

(filter even? (range 10))
;; ⇒ (0 2 4 6 8)
(filter #(if (< (count %) 5) %) ["Paul" "Celery" "Computer" "Rudd" "Tayne"])
;; ⇒ ("Paul" "Rudd")

When using sets with filter, remember that if nil or false is in the set and in the collection, then the predicate will return itself: nil.

In this example, when nil and false are tested with the predicate, the predicate returns nil. This is because if the item is present in the set it is returned. This will cause that item to /not/ be included in the returned lazy-sequence.

(filter #{:nothing :something nil}
       [:nothing :something :things :someone nil false :pigeons])
;; ⇒ (:nothing :something)

remove

remove returns a lazy sequence of items that return false or nil for the provided predicate. Contrast to filter.

(remove even? (range 10))
;; ⇒ (1 3 5 7 9)
;; relative complement. probably useless?
(remove {:a 1 :b 2} [:h :k :z :b :s])
;; ⇒ (:h :k :z :s)

When using sets with remove, remember that if nil or false is in the set and in the collection, then the predicate will return itself: nil. This will cause that item to be included in the returned lazy sequence.

In this example, when nil and false are tested with the predicate, the predicate returns nil. This is because if the item is present in the set it is returned.

(remove #{:nothing :something nil}
        [:nothing :something :things :someone nil false :pigeons])
;; ⇒ (:things :someone nil false :pigeons)

iterate

iterate takes a function and an initial value, returns the result of applying the function on that initial value, then applies the function again on the resultant value, and repeats forever, lazily. Note that the function iterates on the value.

(take 5 (iterate inc 1))
;; ⇒ (1 2 3 4 5)
(defn multiply-by-two
  [value]
  (* 2 value))

(take 10 (iterate multiply-by-two 1))
;; ⇒ (1 2 4 8 16 32 64 128 256 512)

get-in

get-in is used to get a value that is deep inside a data structure.

You have to provide the data structure and a sequence of keys, where a key is valid at each subsequent level of the nested data structure.

If the sequence of keys does not lead to a valid path, nil is returned.

(def family
  {:dad {:shirt 5
         :pants 6
         :shoes 4}
   :mom {:dress {:work 6
                 :casual 7}
         :book 3}
   :son {:toy 5
         :homework 1}})
(get-in family [:dad :shirt])
;; ⇒ 5
(get-in family [:mom :dress])
;; ⇒ {:work 6, :casual 7}
(get-in family [:mom :dress :casual])
;; ⇒ 7
(get-in family [:son :pants])
;; ⇒ nil

```klipse-clojure
(def locations
  [:office :home :school])

(get-in locations [1])
;; ⇒ :home

update-in

update-in is used to update a value deep inside a structure in-place.

Note that since data structures are immutable, it only returns a "modified" data structure, it does not actually alter the original reference.

The "update" function takes the old value and returns a new value which update-in uses in the new modified data structure.

(def family
  {:dad {:shirt 5
         :pants 6
         :shoes 4}
   :mom {:dress {:work 6
                 :casual 7}
         :book 3}
   :son {:toy 5
         :homework 1}})
(update-in family [:dad :pants] inc)

;; ⇒ {:son {:toy 5, :homework 1}, :mom {:dress {:work 6, :casual 7}, :book 3}, :dad {:shoes 4, :shirt 5, :pants 7}}

Notice that "pants" gets incremented

(def locations
  [:office :home :school])

(update-in locations [2] #(keyword (str "high-" (name %))))
;; ⇒ [:office :home :high-school]

assoc-in

assoc-in is used to associate a new value deep inside a structure in-place.

Note that since data structures are immutable, it only returns a "modified" data structure, it does not actually alter the original reference.

Note the difference between update-in and assoc-in: update-in takes a function that applies on the old value to return a new value, whereas assoc-in takes a new value as-is.

(def family
  {:dad {:shirt 5
         :pants 6
         :shoes 4}
   :mom {:dress {:work 6
                 :casual 7}
         :book 3}
   :son {:toy 5
         :homework 1}})
(assoc-in family [:son :crayon] 3)
;; ⇒ {:son {:toy 5, :crayon 3, :homework 1}, :mom {:dress {:work 6, :casual 7}, :book 3}, :dad {:shoes 4, :shirt 5, :pants 6}}
(def locations
  [:office :home :school])

(assoc-in locations [3] :high-school)
;; ⇒ [:office :home :school :high-school]

keys

keys returns a sequence of the keys in a map or record.

(keys {1 "one" 2 "two" 3 "three"})
;; ⇒ (1 2 3)
(defrecord Hand [index middle ring pinky thumb])
(keys (Hand. 2 4 3 1 2))
;; ⇒ (:index :middle :ring :pinky :thumb)

vals

vals returns a sequence of vals in a map or record.

(vals {:meows 20 :barks 2 :moos 5})
;; ⇒ (5 2 20)
(defrecord Hand [index middle ring pinky thumb])
(vals (Hand. 1 2 3 4 5))
;; ⇒ (1 2 3 4 5)

select-keys

select-keys is used to extract a subset of a map:

(def family
  {:dad {:shirt 5
         :pant 6
         :shoes 4}
   :mom {:dress {:work 6
                 :casual 7}
         :book 3}
   :son {:toy 5
         :homework 1}})
(select-keys family [:dad])
;; ⇒ {:dad {:shoes 4, :shirt 5, :pant 6}}
(select-keys family [:mom :son])
;; ⇒ {:son {:toy 5, :homework 1}, :mom {:dress {:work 6, :casual 7}, :book 3}}

take

take returns a lazy sequence of the first n items of a collection coll.

(take 3 [1 3 5 7 9])
;; ⇒ (1 3 5)
(type (take 3 (range)))
;; ⇒ clojure.lang.LazySeq

If there are fewer than n items in coll, all items will be returned.

(take 5 [1 2 3])
;; ⇒ (1 2 3)
(take 3 nil)
;; ⇒ ()

drop

drop drops n items from a collection coll and returns a lazy sequence of the rest of it.

(drop 3 '(0 1 2 3 4 5 6))
;; ⇒ (3 4 5 6)
(drop 2 [1 2])
;; ⇒ ()
(drop 2 nil)
;; ⇒ ()

take-while

take-while returns a lazy sequence of items from a collection as long as the predicate returns true for each item:

(take-while #(< % 5) (range))
;; ⇒ (0 1 2 3 4)

drop-while

drop-while drops items from a collection as long as the predicate returns false for the item and when the first non-false item is found, it returns a lazy sequence from that item onwards:

(drop-while #(< % 5) (range 10))
;; ⇒ (5 6 7 8 9)

Transients

Clojure data structures are immutable, they do not change. Mutating them produces a new data structure that internally has structural sharing with the original one. This makes a whole class of concurrency hazards go away but has some performance penalty and additional GC pressure.

For cases when raw performance for a piece of code is more important than safety, Clojure provides mutable versions of vectors and unsorted maps. They are known as transients and should only be used for locals and as an optimization technique after profiling.

Transients are produced from immutable data structures using the clojure.core/transient function:

(let [m (transient {})]
  (assoc! m :key "value") ;; mutates the transient in place!
  (count m))
;; ⇒ 1

Note that clojure.core/transient does not affect nested collections, for example, values in a map of keywords to vectors.

To mutate transients, use clojure.core/assoc!, clojure.core/dissoc! and clojure.core/conj!. The exclamation point at the end hints that these functions work on transients and modify data structures in place, which is not safe of data structures are shared between threads.

To create an immutable data structure out of a transient, use clojure.core/persistent!:

(let [m (transient {})]
        (assoc! m :key "value")
        (persistent! m)) ;; ⇒ {:key "value"}

In conclusion: use transients only as an optimization technique and only after profiling and identifying hot spots in your code. Guessing is the shortest way we know to blowing the performance.

Custom Collections and Sequences

It is possible to develop custom collection types in Clojure or Java and have clojure.core functions work on them just like they do on builtin types.

TBD: How to Contribute

Wrapping Up

When working with Clojure, it is common to operate and transform collections and sequences. Clojure's core library unify operations on collections and sequences where possible. This extends to Java collections, arrays and iterable objects for seamless interoperability.

Most of the time, whenever you need a function that transforms sequences, chances are, there is one already that does that in clojure.core or you can compose more than one clojure.core function to achieve the same result.

Contributors

Michael Klishin michael@defprotocol.org Robert Randolph audiolabs@gmail.com satoru satorulogic@gmail.com