Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting Java collections to Clojure data structures

I am creating a Clojure interface to a Java API with a method that returns a java.util.LinkedHashSet.

Firstly, is the idiomatic Clojure way of handling this to convert the LinkedHashSet to a clojure data structure?

Secondly, what is the best method for converting Java collections into Clojure data structures?

like image 659
Toby Hede Avatar asked Feb 14 '12 10:02

Toby Hede


3 Answers

There are lots of options, since Clojure plays very nicely with Java collections. It depends on exactly what data structure you want to use in Clojure.

Here's some examples:

;; create a HashSet
(def a (java.util.HashSet.))
(dotimes [i 10] (.add a i))

;; Get all the values as a sequence 
(seq a)
=> (0 1 2 3 4 5 6 7 8 9)

;; build a new HashSet containing the values from a 
(into #{} a)
#{0 1 2 3 4 5 6 7 8 9}

;; Just use the HashSet directly (high performance, no copy required)
(.contains a 1)
=> true
(.contains a 100)
=> false

Regarding when to use each of these, I'd suggest the following advice:

  • If you are trying to wrap a Java library and present a clean Clojure API, then I'd suggest converting to the equivalent Clojure data structures. This is what Clojure users will expect, and you can hide the potentially messy Java interop details. As a bonus, this will make things immutable so that you don't run the risk of Java collections mutating while you use them.
  • If you just want to use the Java API quickly and efficiently, just use Java interop directly on the Java collections.
like image 59
mikera Avatar answered Nov 11 '22 00:11

mikera


The idiomatic way to convert java collections to clojure is to use the (seq) function, which is already called by most functions operating on sequences.

(def s (java.util.LinkedHashSet.))
#'user/s
user> (seq s)
nil
user> (.add s "foo")
true
user> (seq s)
("foo")
user> 
like image 26
Joost Diepenmaat Avatar answered Nov 10 '22 23:11

Joost Diepenmaat


I honestly don't know if there's a universally accepted practice, but here's Chris Houser arguing against Java to Clojure adapters as you break compatibility with the original Java API.

To perform the translation you asked for, simply use into:

user=> (import java.util.LinkedHashSet)
java.util.LinkedHashSet
user=> (def x (LinkedHashSet.))
#'user/x
user=> (.add x "test")
true
user=> (def y (into #{} x))
#'user/y
user=> y
#{"test"}
like image 22
skuro Avatar answered Nov 11 '22 00:11

skuro