Explore Clojure's powerful collection manipulation functions, including conj, assoc, dissoc, update, merge, into, get, contains?, and keys/vals. Learn how these functions interact with lists, vectors, maps, and sets, with examples and comparisons to Java.
In Clojure, collections are immutable and persistent, meaning that any operation on a collection returns a new collection rather than modifying the original. This immutability is a cornerstone of functional programming, offering benefits such as thread safety and easier reasoning about code. For Java developers, accustomed to mutable collections, this shift can be both challenging and liberating. In this section, we’ll explore Clojure’s collection manipulation functions, drawing parallels to Java where applicable, and providing examples to illustrate their use.
Clojure provides several core collection types:
HashMap.HashSet.Each collection type has specific functions optimized for its structure, but many functions are polymorphic, meaning they can operate on multiple types of collections.
Let’s delve into some of the most commonly used functions for manipulating collections in Clojure.
conjThe conj function is used to add elements to a collection. Its behavior varies slightly depending on the collection type:
1;; Adding to a list
2(def my-list '(1 2 3))
3(def new-list (conj my-list 0))
4;; new-list => (0 1 2 3)
5
6;; Adding to a vector
7(def my-vector [1 2 3])
8(def new-vector (conj my-vector 4))
9;; new-vector => [1 2 3 4]
10
11;; Adding to a set
12(def my-set #{1 2 3})
13(def new-set (conj my-set 4))
14;; new-set => #{1 2 3 4}
In Java, adding elements to collections typically involves methods like add or put, which mutate the collection. In Clojure, conj returns a new collection, preserving immutability.
assocThe assoc function is used to associate a key with a value in a map or to update an element at a specific index in a vector.
1;; Associating a key with a value in a map
2(def my-map {:a 1 :b 2})
3(def new-map (assoc my-map :c 3))
4;; new-map => {:a 1, :b 2, :c 3}
5
6;; Updating an element in a vector
7(def my-vector [1 2 3])
8(def updated-vector (assoc my-vector 1 10))
9;; updated-vector => [1 10 3]
In Java, updating a map involves using put, which modifies the map in place. Clojure’s assoc returns a new map with the updated key-value pair.
dissocThe dissoc function removes a key from a map.
1(def my-map {:a 1 :b 2 :c 3})
2(def smaller-map (dissoc my-map :b))
3;; smaller-map => {:a 1, :c 3}
Java’s remove method on maps is similar, but again, it mutates the original map, whereas dissoc returns a new map.
updateThe update function applies a function to the value associated with a key in a map.
1(def my-map {:a 1 :b 2})
2(def updated-map (update my-map :b inc))
3;; updated-map => {:a 1, :b 3}
This is akin to retrieving a value from a map, modifying it, and then putting it back, but update does this in a single, atomic operation.
mergeThe merge function combines multiple maps into one.
1(def map1 {:a 1 :b 2})
2(def map2 {:b 3 :c 4})
3(def merged-map (merge map1 map2))
4;; merged-map => {:a 1, :b 3, :c 4}
In Java, merging maps typically involves iterating over one map and inserting its entries into another. Clojure’s merge simplifies this process.
intoThe into function is used to add all elements from one collection into another.
1(def vector1 [1 2 3])
2(def vector2 [4 5 6])
3(def combined-vector (into vector1 vector2))
4;; combined-vector => [1 2 3 4 5 6]
This is similar to Java’s addAll method for collections, but into works with any Clojure collection type.
getThe get function retrieves the value associated with a key in a map or an index in a vector.
1(def my-map {:a 1 :b 2})
2(def value (get my-map :a))
3;; value => 1
4
5(def my-vector [10 20 30])
6(def element (get my-vector 1))
7;; element => 20
In Java, you would use get on a map or get on a list, but Clojure’s get is polymorphic and works across different collection types.
contains?The contains? function checks if a map contains a specific key or if a set contains a specific element.
1(def my-map {:a 1 :b 2})
2(def has-key (contains? my-map :a))
3;; has-key => true
4
5(def my-set #{1 2 3})
6(def has-element (contains? my-set 2))
7;; has-element => true
Java’s containsKey and contains methods are similar, but contains? is more versatile.
keys and valsThe keys and vals functions return the keys and values of a map, respectively.
1(def my-map {:a 1 :b 2 :c 3})
2(def map-keys (keys my-map))
3;; map-keys => (:a :b :c)
4
5(def map-vals (vals my-map))
6;; map-vals => (1 2 3)
In Java, you would use keySet and values methods on a map to achieve similar results.
Let’s compare how some of these operations differ between Clojure and Java:
1// Java: Adding elements to a list
2List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
3list.add(0, 0); // Modifies the list in place
4
5// Clojure: Adding elements to a list
6(def my-list '(1 2 3))
7(def new-list (conj my-list 0)) ; Returns a new list
1// Java: Updating a map
2Map<String, Integer> map = new HashMap<>();
3map.put("a", 1);
4map.put("b", 2);
5map.put("c", 3); // Modifies the map in place
6
7// Clojure: Updating a map
8(def my-map {:a 1 :b 2})
9(def new-map (assoc my-map :c 3)) ; Returns a new map
Experiment with the following code snippets to deepen your understanding of Clojure’s collection manipulation functions:
conj: Try adding multiple elements at once to a vector or list.assoc with vectors: Update multiple indices in a vector.merge: Merge more than two maps and observe the behavior when keys overlap.into: Use into to combine different types of collections, such as a set into a vector.Below is a diagram illustrating how conj behaves differently with lists and vectors:
graph TD;
A["Original List: (1 2 3)"] -->|conj 0| B["New List: (0 1 2 3)"];
C[Original Vector: [1 2 3]] -->|conj 4| D[New Vector: [1 2 3 4]];
Diagram 1: The conj function adds elements to the front of a list and the end of a vector.
assoc.conj, assoc, and merge allow for expressive and concise manipulation of collections.For further reading, explore the Official Clojure Documentation and ClojureDocs.