alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (data_structures.clj)

This example Java source code file (data_structures.clj) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

banana, classcastexception, empty, illegalargumentexception, illegalstateexception, integer, lists, long, maps, object, rec, runtimeexception, sets

The data_structures.clj Java example source code

;   Copyright (c) Rich Hickey. All rights reserved.
;   The use and distribution terms for this software are covered by the
;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;   which can be found in the file epl-v10.html at the root of this distribution.
;   By using this software in any fashion, you are agreeing to be bound by
;   the terms of this license.
;   You must not remove this notice, or any other, from this software.

; Author: Frantisek Sodomka


(ns clojure.test-clojure.data-structures
  (:use clojure.test
        [clojure.test.generative :exclude (is)])
  (:require [clojure.test-clojure.generators :as cgen]
            [clojure.data.generators :as gen]
            [clojure.string :as string]))


;; *** Helper functions ***

(defn diff [s1 s2]
  (seq (reduce disj (set s1) (set s2))))


;; *** Generative ***
(defspec subcollection-counts-are-consistent
  identity
  [^{:tag cgen/ednable-collection} coll]
  (let [n (count coll)]
    (dotimes [i n]
      (is (= n
             (+ i (count (nthnext coll i)))
             (+ i (count (drop i coll))))))))

(defn- transient? [x]
  (instance? clojure.lang.ITransientCollection x))

(defn gen-transient-set-action []
  (gen/rand-nth [[#(conj! %1 %2) #(conj %1 %2) (gen/uniform -100 100)]
                 [#(disj! %1 %2) #(disj %1 %2) (gen/uniform -100 100)]
                 [#(deref (future (conj! %1 %2))) #(conj %1 %2) (gen/uniform -100 100)]
                 [#(deref (future (disj! %1 %2))) #(disj %1 %2) (gen/uniform -100 100)]
                 [persistent! identity]
                 [identity transient]]))

(defn gen-transient-set-actions []
  (gen/reps #(gen/uniform 0 100) gen-transient-set-action))

(defn tempty? [t]
  (= (count t) 0))

(defn gen-transient-vector-action []
  (gen/rand-nth [[#(conj! %1 %2) #(conj %1 %2) (gen/uniform -100 100)]
                 [(fn [v _] (if (tempty? v) v (pop! v)))
                  (fn [v _] (if (tempty? v) v (pop v)))
                  (gen/uniform -100 100)]
                 [#(deref (future (conj! %1 %2))) #(conj %1 %2) (gen/uniform -100 100)]
                 [(fn [v _] (if (tempty? v) v (deref (future (pop! v)))))
                  (fn [v _] (if (tempty? v) v (pop v)))
                  (gen/uniform -100 100)]
                 [persistent! identity]
                 [identity transient]]))

(defn gen-transient-vector-actions []
  (gen/reps #(gen/uniform 0 100) gen-transient-vector-action))

(defn gen-transient-map-action []
  (gen/rand-nth [[#(assoc! %1 %2 %2) #(assoc %1 %2 %2) (gen/uniform -100 100)]
                 [#(dissoc! %1 %2) #(dissoc %1 %2) (gen/uniform -100 100)]
                 [#(deref (future (assoc! %1 %2 %2))) #(assoc %1 %2 %2) (gen/uniform -100 100)]
                 [#(deref (future (dissoc! %1 %2))) #(dissoc %1 %2) (gen/uniform -100 100)]
                 [persistent! identity]
                 [identity transient]]))

(defn gen-transient-map-actions []
  (gen/reps #(gen/uniform 0 100) gen-transient-map-action))

(defn assert-same-collection [a b]
  (assert (= (count a) (count b) (.size a) (.size b)))
  (assert (= a b))
  (assert (= b a))
  (assert (.equals ^Object a b))
  (assert (.equals ^Object b a))
  (assert (= (hash a) (hash b)))
  (assert (= (.hashCode ^Object a) (.hashCode ^Object b)))
  (assert (= a
             (into (empty a) a)
             (into (empty b) b)
             (into (empty a) b)
             (into (empty b) a))))

(defn apply-actions [coll actions]
  (reduce (fn [c [tfunc pfunc & args]]
            (apply (if (transient? c) tfunc pfunc) c args))
          coll
          actions))

(defn to-persistent [c]
  (if (transient? c) (persistent! c) c))

(defspec same-output-persistent-transient-set
  identity
  [^{:tag clojure.test-clojure.data-structures/gen-transient-set-actions} actions]
  (assert-same-collection
   (to-persistent (apply-actions #{} actions))
   (to-persistent (apply-actions #{} actions))))

(defspec same-output-persistent-transient-vector
  identity
  [^{:tag clojure.test-clojure.data-structures/gen-transient-vector-actions} actions]
  (assert-same-collection
   (to-persistent (apply-actions [] actions))
   (to-persistent (apply-actions [] actions))))

(defspec same-output-persistent-transient-map
  identity
  [^{:tag clojure.test-clojure.data-structures/gen-transient-map-actions} actions]
  (assert-same-collection
   (to-persistent (apply-actions clojure.lang.PersistentArrayMap/EMPTY actions))
   (to-persistent (apply-actions clojure.lang.PersistentArrayMap/EMPTY actions)))
  (assert-same-collection
   (to-persistent (apply-actions clojure.lang.PersistentHashMap/EMPTY actions))
   (to-persistent (apply-actions clojure.lang.PersistentHashMap/EMPTY actions))))

;; *** General ***

(defstruct equality-struct :a :b)

(deftest test-equality
  ; nil is not equal to any other value
  (are [x] (not (= nil x))
      true false
      0 0.0
      \space
      "" #""
      () [] #{} {}
      (lazy-seq nil)  ; SVN 1292: fixed (= (lazy-seq nil) nil)
      (lazy-seq ())
      (lazy-seq [])
      (lazy-seq {})
      (lazy-seq #{})
      (lazy-seq "")
      (lazy-seq (into-array []))
      (new Object) )

  ; numbers equality across types (see tests below - NOT IMPLEMENTED YET)

  ; ratios
  (is (== 1/2 0.5))
  (is (== 1/1000 0.001))
  (is (not= 2/3 0.6666666666666666))

  ; vectors equal other seqs by items equality
  (are [x y] (= x y)
      '() []        ; regression fixed in r1208; was not equal
      '(1) [1]
      '(1 2) [1 2]

      [] '()        ; same again, but vectors first
      [1] '(1)
      [1 2] '(1 2) )
  (is (not= [1 2] '(2 1)))  ; order of items matters

  ; list and vector vs. set and map
  (are [x y] (not= x y)
      ; only () equals []
      () #{}
      () {}
      [] #{}
      [] {}
      #{} {}
      ; only '(1) equals [1]
      '(1) #{1}
      [1] #{1} )

  ; sorted-map, hash-map and array-map - classes differ, but content is equal
  
;; TODO: reimplement all-are with new do-template?  
;;   (all-are (not= (class _1) (class _2))
;;       (sorted-map :a 1)
;;       (hash-map   :a 1)
;;       (array-map  :a 1))
;;   (all-are (= _1 _2)
;;       (sorted-map)
;;       (hash-map)
;;       (array-map))
;;   (all-are (= _1 _2)
;;       (sorted-map :a 1)
;;       (hash-map   :a 1)
;;       (array-map  :a 1))
;;   (all-are (= _1 _2)
;;       (sorted-map :a 1 :z 3 :c 2)
;;       (hash-map   :a 1 :z 3 :c 2)
;;       (array-map  :a 1 :z 3 :c 2))

  ; struct-map vs. sorted-map, hash-map and array-map
  (are [x] (and (not= (class (struct equality-struct 1 2)) (class x))
                (= (struct equality-struct 1 2) x))
      (sorted-map-by compare :a 1 :b 2)
      (sorted-map :a 1 :b 2)
      (hash-map   :a 1 :b 2)
      (array-map  :a 1 :b 2))

  ; sorted-set vs. hash-set
  (is (not= (class (sorted-set 1)) (class (hash-set 1))))
  (are [x y] (= x y)
      (sorted-set-by <) (hash-set)
      (sorted-set-by < 1) (hash-set 1)
      (sorted-set-by < 3 2 1) (hash-set 3 2 1)
      (sorted-set) (hash-set)
      (sorted-set 1) (hash-set 1)
      (sorted-set 3 2 1) (hash-set 3 2 1) ))


;; *** Collections ***

(deftest test-count
  (let [EMPTY clojure.lang.PersistentQueue/EMPTY]
    (are [x y] (= (count x) y)
         EMPTY 0 
         (into EMPTY [:a :b]) 2
         (-> (into EMPTY [:a :b]) pop pop) 0
         
         nil 0

         () 0
         '(1) 1
         '(1 2 3) 3

         [] 0
         [1] 1
         [1 2 3] 3

         #{} 0
         #{1} 1
         #{1 2 3} 3

         {} 0
         {:a 1} 1
         {:a 1 :b 2 :c 3} 3

         "" 0
         "a" 1
         "abc" 3

         (into-array []) 0
         (into-array [1]) 1
         (into-array [1 2 3]) 3

         (java.util.ArrayList. []) 0
         (java.util.ArrayList. [1]) 1
         (java.util.ArrayList. [1 2 3]) 3

         (java.util.HashMap. {}) 0
         (java.util.HashMap. {:a 1}) 1
         (java.util.HashMap. {:a 1 :b 2 :c 3}) 3 ))

  ; different types
  (are [x]  (= (count [x]) 1)
      nil true false
      0 0.0 "" \space
      () [] #{} {}  ))


(deftest test-conj
  ; doesn't work on strings or arrays
  (is (thrown? ClassCastException (conj "" \a)))
  (is (thrown? ClassCastException (conj (into-array []) 1)))

  (are [x y] (= x y)
      (conj nil 1) '(1)
      (conj nil 3 2 1) '(1 2 3)

      (conj nil nil) '(nil)
      (conj nil nil nil) '(nil nil)
      (conj nil nil nil 1) '(1 nil nil)

      ; list -> conj puts the item at the front of the list
      (conj () 1) '(1)
      (conj () 1 2) '(2 1)

      (conj '(2 3) 1) '(1 2 3)
      (conj '(2 3) 1 4 3) '(3 4 1 2 3)

      (conj () nil) '(nil)
      (conj () ()) '(())

      ; vector -> conj puts the item at the end of the vector
      (conj [] 1) [1]
      (conj [] 1 2) [1 2]

      (conj [2 3] 1) [2 3 1]
      (conj [2 3] 1 4 3) [2 3 1 4 3]

      (conj [] nil) [nil]
      (conj [] []) [[]]

      ; map -> conj expects another (possibly single entry) map as the item,
      ;   and returns a new map which is the old map plus the entries
      ;   from the new, which may overwrite entries of the old.
      ;   conj also accepts a MapEntry or a vector of two items (key and value).
      (conj {} {}) {}
      (conj {} {:a 1}) {:a 1}
      (conj {} {:a 1 :b 2}) {:a 1 :b 2}
      (conj {} {:a 1 :b 2} {:c 3}) {:a 1 :b 2 :c 3}
      (conj {} {:a 1 :b 2} {:a 3 :c 4}) {:a 3 :b 2 :c 4}

      (conj {:a 1} {:a 7}) {:a 7}
      (conj {:a 1} {:b 2}) {:a 1 :b 2}
      (conj {:a 1} {:a 7 :b 2}) {:a 7 :b 2}
      (conj {:a 1} {:a 7 :b 2} {:c 3}) {:a 7 :b 2 :c 3}
      (conj {:a 1} {:a 7 :b 2} {:b 4 :c 5}) {:a 7 :b 4 :c 5}

      (conj {} (first {:a 1})) {:a 1}           ; MapEntry
      (conj {:a 1} (first {:b 2})) {:a 1 :b 2}
      (conj {:a 1} (first {:a 7})) {:a 7}
      (conj {:a 1} (first {:b 2}) (first {:a 5})) {:a 5 :b 2}

      (conj {} [:a 1]) {:a 1}                   ; vector
      (conj {:a 1} [:b 2]) {:a 1 :b 2}
      (conj {:a 1} [:a 7]) {:a 7}
      (conj {:a 1} [:b 2] [:a 5]) {:a 5 :b 2}

      (conj {} {nil {}}) {nil {}}
      (conj {} {{} nil}) {{} nil}
      (conj {} {{} {}}) {{} {}}

      ; set
      (conj #{} 1) #{1}
      (conj #{} 1 2 3) #{1 2 3}

      (conj #{2 3} 1) #{3 1 2}
      (conj #{3 2} 1) #{1 2 3}

      (conj #{2 3} 2) #{2 3}
      (conj #{2 3} 2 3) #{2 3}
      (conj #{2 3} 4 1 2 3) #{1 2 3 4}

      (conj #{} nil) #{nil}
      (conj #{} #{}) #{#{}} ))


;; *** Lists and Vectors ***

(deftest test-peek
  ; doesn't work for sets and maps
  (is (thrown? ClassCastException (peek #{1})))
  (is (thrown? ClassCastException (peek {:a 1})))

  (are [x y] (= x y)
      (peek nil) nil

      ; list = first
      (peek ()) nil
      (peek '(1)) 1
      (peek '(1 2 3)) 1

      (peek '(nil)) nil     ; special cases
      (peek '(1 nil)) 1
      (peek '(nil 2)) nil
      (peek '(())) ()
      (peek '(() nil)) ()
      (peek '(() 2 nil)) ()

      ; vector = last
      (peek []) nil
      (peek [1]) 1
      (peek [1 2 3]) 3

      (peek [nil]) nil      ; special cases
      (peek [1 nil]) nil
      (peek [nil 2]) 2
      (peek [[]]) []
      (peek [[] nil]) nil
      (peek [[] 2 nil]) nil ))


(deftest test-pop
  ; doesn't work for sets and maps
  (is (thrown? ClassCastException (pop #{1})))
  (is (thrown? ClassCastException (pop #{:a 1})))

  ; collection cannot be empty
  (is (thrown? IllegalStateException (pop ())))
  (is (thrown? IllegalStateException (pop [])))

  (are [x y] (= x y)
      (pop nil) nil

      ; list - pop first
      (pop '(1)) ()
      (pop '(1 2 3)) '(2 3)

      (pop '(nil)) ()
      (pop '(1 nil)) '(nil)
      (pop '(nil 2)) '(2)
      (pop '(())) ()
      (pop '(() nil)) '(nil)
      (pop '(() 2 nil)) '(2 nil)

      ; vector - pop last
      (pop [1]) []
      (pop [1 2 3]) [1 2]

      (pop [nil]) []
      (pop [1 nil]) [1]
      (pop [nil 2]) [nil]
      (pop [[]]) []
      (pop [[] nil]) [[]]
      (pop [[] 2 nil]) [[] 2] ))


;; *** Lists (IPersistentList) ***

(deftest test-list
  (are [x]  (list? x)
      ()
      '()
      (list)
      (list 1 2 3) )

  ; order is important
  (are [x y] (not (= x y))
      (list 1 2) (list 2 1)
      (list 3 1 2) (list 1 2 3) )

  (are [x y] (= x y)
      '() ()
      (list) '()
      (list 1) '(1)
      (list 1 2) '(1 2)

      ; nesting
      (list 1 (list 2 3) (list 3 (list 4 5 (list 6 (list 7)))))
        '(1 (2 3) (3 (4 5 (6 (7)))))

      ; different data structures
      (list true false nil)
        '(true false nil)
      (list 1 2.5 2/3 "ab" \x 'cd :kw)
        '(1 2.5 2/3 "ab" \x cd :kw)
      (list (list 1 2) [3 4] {:a 1 :b 2} #{:c :d})
        '((1 2) [3 4] {:a 1 :b 2} #{:c :d})

      ; evaluation
      (list (+ 1 2) [(+ 2 3) 'a] (list (* 2 3) 8))
        '(3 [5 a] (6 8))

      ; special cases
      (list nil) '(nil)
      (list 1 nil) '(1 nil)
      (list nil 2) '(nil 2)
      (list ()) '(())
      (list 1 ()) '(1 ())
      (list () 2) '(() 2) ))


;; *** Maps (IPersistentMap) ***

(deftest test-find
  (are [x y] (= x y)
      (find {} :a) nil

      (find {:a 1} :a) [:a 1]
      (find {:a 1} :b) nil
      (find {nil 1} nil) [nil 1]

      (find {:a 1 :b 2} :a) [:a 1]
      (find {:a 1 :b 2} :b) [:b 2]
      (find {:a 1 :b 2} :c) nil

      (find {} nil) nil
      (find {:a 1} nil) nil
      (find {:a 1 :b 2} nil) nil ))


(deftest test-contains?
  ; contains? is designed to work preferably on maps and sets
  (are [x y] (= x y)
      (contains? {} :a) false
      (contains? {} nil) false

      (contains? {:a 1} :a) true
      (contains? {:a 1} :b) false
      (contains? {:a 1} nil) false
      (contains? {nil 1} nil) true

      (contains? {:a 1 :b 2} :a) true
      (contains? {:a 1 :b 2} :b) true
      (contains? {:a 1 :b 2} :c) false
      (contains? {:a 1 :b 2} nil) false

      ; sets
      (contains? #{} 1) false
      (contains? #{} nil) false

      (contains? #{1} 1) true
      (contains? #{1} 2) false
      (contains? #{1} nil) false

      (contains? #{1 2 3} 1) true
      (contains? #{1 2 3} 3) true
      (contains? #{1 2 3} 10) false
      (contains? #{1 2 3} nil) false)

  ; contains? also works on java.util.Map and java.util.Set.
  (are [x y] (= x y)
      (contains? (java.util.HashMap. {}) :a) false
      (contains? (java.util.HashMap. {}) nil) false

      (contains? (java.util.HashMap. {:a 1}) :a) true
      (contains? (java.util.HashMap. {:a 1}) :b) false
      (contains? (java.util.HashMap. {:a 1}) nil) false

      (contains? (java.util.HashMap. {:a 1 :b 2}) :a) true
      (contains? (java.util.HashMap. {:a 1 :b 2}) :b) true
      (contains? (java.util.HashMap. {:a 1 :b 2}) :c) false
      (contains? (java.util.HashMap. {:a 1 :b 2}) nil) false

      ; sets
      (contains? (java.util.HashSet. #{}) 1) false
      (contains? (java.util.HashSet. #{}) nil) false

      (contains? (java.util.HashSet. #{1}) 1) true
      (contains? (java.util.HashSet. #{1}) 2) false
      (contains? (java.util.HashSet. #{1}) nil) false

      (contains? (java.util.HashSet. #{1 2 3}) 1) true
      (contains? (java.util.HashSet. #{1 2 3}) 3) true
      (contains? (java.util.HashSet. #{1 2 3}) 10) false
      (contains? (java.util.HashSet. #{1 2 3}) nil) false)

  ; numerically indexed collections (e.g. vectors and Java arrays)
  ; => test if the numeric key is WITHIN THE RANGE OF INDEXES
  (are [x y] (= x y)
      (contains? [] 0) false
      (contains? [] -1) false
      (contains? [] 1) false

      (contains? [1] 0) true
      (contains? [1] -1) false
      (contains? [1] 1) false

      (contains? [1 2 3] 0) true
      (contains? [1 2 3] 2) true
      (contains? [1 2 3] 3) false
      (contains? [1 2 3] -1) false

      ; arrays
      (contains? (into-array []) 0) false
      (contains? (into-array []) -1) false
      (contains? (into-array []) 1) false

      (contains? (into-array [1]) 0) true
      (contains? (into-array [1]) -1) false
      (contains? (into-array [1]) 1) false

      (contains? (into-array [1 2 3]) 0) true
      (contains? (into-array [1 2 3]) 2) true
      (contains? (into-array [1 2 3]) 3) false
      (contains? (into-array [1 2 3]) -1) false)

  ; 'contains?' will not operate on non-associative things
  (are [x]  (is (thrown? Exception (contains? x 1)))
       '(1 2 3)
       3))


(deftest test-keys
  (are [x y] (= x y)      ; other than map data structures
      (keys ()) nil
      (keys []) nil
      (keys #{}) nil
      (keys "") nil )

  (are [x y] (= x y)
      ; (class {:a 1}) => clojure.lang.PersistentArrayMap
      (keys {}) nil
      (keys {:a 1}) '(:a)
      (keys {nil 1}) '(nil)
      (diff (keys {:a 1 :b 2}) '(:a :b)) nil              ; (keys {:a 1 :b 2}) '(:a :b)

      ; (class (sorted-map :a 1)) => clojure.lang.PersistentTreeMap
      (keys (sorted-map)) nil
      (keys (sorted-map :a 1)) '(:a)
      (diff (keys (sorted-map :a 1 :b 2)) '(:a :b)) nil   ; (keys (sorted-map :a 1 :b 2)) '(:a :b)

      ; (class (hash-map :a 1)) => clojure.lang.PersistentHashMap
      (keys (hash-map)) nil
      (keys (hash-map :a 1)) '(:a)
      (diff (keys (hash-map :a 1 :b 2)) '(:a :b)) nil )   ; (keys (hash-map :a 1 :b 2)) '(:a :b)

  (let [m {:a 1 :b 2}
        k (keys m)]
    (is (= {:hi :there} (meta (with-meta k {:hi :there}))))))


(deftest test-vals
  (are [x y] (= x y)      ; other than map data structures
      (vals ()) nil
      (vals []) nil
      (vals #{}) nil
      (vals "") nil )

  (are [x y] (= x y)
      ; (class {:a 1}) => clojure.lang.PersistentArrayMap
      (vals {}) nil
      (vals {:a 1}) '(1)
      (vals {nil 1}) '(1)
      (diff (vals {:a 1 :b 2}) '(1 2)) nil              ; (vals {:a 1 :b 2}) '(1 2)

      ; (class (sorted-map :a 1)) => clojure.lang.PersistentTreeMap
      (vals (sorted-map)) nil
      (vals (sorted-map :a 1)) '(1)
      (diff (vals (sorted-map :a 1 :b 2)) '(1 2)) nil   ; (vals (sorted-map :a 1 :b 2)) '(1 2)

      ; (class (hash-map :a 1)) => clojure.lang.PersistentHashMap
      (vals (hash-map)) nil
      (vals (hash-map :a 1)) '(1)
      (diff (vals (hash-map :a 1 :b 2)) '(1 2)) nil )   ; (vals (hash-map :a 1 :b 2)) '(1 2)

  (let [m {:a 1 :b 2}
        v (vals m)]
    (is (= {:hi :there} (meta (with-meta v {:hi :there}))))))


(deftest test-key
  (are [x]  (= (key (first (hash-map x :value))) x)
      nil
      false true
      0 42
      0.0 3.14
      2/3
      0M 1M
      \c
      "" "abc"
      'sym
      :kw
      () '(1 2)
      [] [1 2]
      {} {:a 1 :b 2}
      #{} #{1 2} ))


(deftest test-val
  (are [x]  (= (val (first (hash-map :key x))) x)
      nil
      false true
      0 42
      0.0 3.14
      2/3
      0M 1M
      \c
      "" "abc"
      'sym
      :kw
      () '(1 2)
      [] [1 2]
      {} {:a 1 :b 2}
      #{} #{1 2} ))

(deftest test-get
  (let [m {:a 1, :b 2, :c {:d 3, :e 4}, :f nil, :g false, nil {:h 5}}]
    (is (thrown? IllegalArgumentException (get-in {:a 1} 5)))
    (are [x y] (= x y)
         (get m :a) 1
         (get m :e) nil
         (get m :e 0) 0
         (get m nil) {:h 5}
         (get m :b 0) 2
         (get m :f 0) nil

         (get-in m [:c :e]) 4
         (get-in m '(:c :e)) 4
         (get-in m [:c :x]) nil
         (get-in m [:f]) nil
         (get-in m [:g]) false
         (get-in m [:h]) nil
         (get-in m []) m
         (get-in m nil) m

         (get-in m [:c :e] 0) 4
         (get-in m '(:c :e) 0) 4
         (get-in m [:c :x] 0) 0
         (get-in m [:b] 0) 2
         (get-in m [:f] 0) nil
         (get-in m [:g] 0) false
         (get-in m [:h] 0) 0
         (get-in m [:x :y] {:y 1}) {:y 1}
         (get-in m [] 0) m
         (get-in m nil 0) m)))

(deftest test-nested-map-destructuring
  (let [sample-map {:a 1 :b {:a 2}}
        {ao1 :a {ai1 :a} :b} sample-map
        {ao2 :a {ai2 :a :as m1} :b :as m2} sample-map
        {ao3 :a {ai3 :a :as m} :b :as m} sample-map
        {{ai4 :a :as m} :b ao4 :a :as m} sample-map]
    (are [i o] (and (= i 2)
                    (= o 1))
         ai1 ao1
         ai2 ao2
         ai3 ao3
         ai4 ao4)))

(deftest test-map-entry?
  (testing "map-entry? = false"
    (are [entry]
      (false? (map-entry? entry))
      nil 5 #{1 2} '(1 2) {:a 1} [] [0] [1 2 3]))
  (testing "map-entry? = true"
    (are [entry]
      (true? (map-entry? entry))
      (first (doto (java.util.HashMap.) (.put "x" 1))))))

;; *** Sets ***

(deftest test-hash-set
  (are [x] (set? x)
      #{}
      #{1 2}
      (hash-set)
      (hash-set 1 2) )

  ; order isn't important
  (are [x y] (= x y)
      #{1 2} #{2 1}
      #{3 1 2} #{1 2 3}
      (hash-set 1 2) (hash-set 2 1)
      (hash-set 3 1 2) (hash-set 1 2 3) )


  (are [x y] (= x y)
      ; equal classes
      (class #{}) (class (hash-set))
      (class #{1 2}) (class (hash-set 1 2))

      ; creating
      (hash-set) #{}
      (hash-set 1) #{1}
      (hash-set 1 2) #{1 2}

      ; nesting
      (hash-set 1 (hash-set 2 3) (hash-set 3 (hash-set 4 5 (hash-set 6 (hash-set 7)))))
        #{1 #{2 3} #{3 #{4 5 #{6 #{7}}}}}

      ; different data structures
      (hash-set true false nil)
        #{true false nil}
      (hash-set 1 2.5 2/3 "ab" \x 'cd :kw)
        #{1 2.5 2/3 "ab" \x 'cd :kw}
      (hash-set (list 1 2) [3 4] {:a 1 :b 2} #{:c :d})
        #{'(1 2) [3 4] {:a 1 :b 2} #{:c :d}}

      ; evaluation
      (hash-set (+ 1 2) [(+ 2 3) :a] (hash-set (* 2 3) 8))
        #{3 [5 :a] #{6 8}}

      ; special cases
      (hash-set nil) #{nil}
      (hash-set 1 nil) #{1 nil}
      (hash-set nil 2) #{nil 2}
      (hash-set #{}) #{#{}}
      (hash-set 1 #{}) #{1 #{}}
      (hash-set #{} 2) #{#{} 2}
      (hash-set (Integer. -1)) (hash-set (Long. -1))))


(deftest test-sorted-set
  ; only compatible types can be used
  (is (thrown? ClassCastException (sorted-set 1 "a")))
  (is (thrown? ClassCastException (sorted-set '(1 2) [3 4])))

  ; creates set?
  (are [x] (set? x)
       (sorted-set)
       (sorted-set 1 2) )

  ; equal and unique
  (are [x] (and (= (sorted-set x) #{x})
                (= (sorted-set x x) (sorted-set x)))
      nil
      false true
      0 42
      0.0 3.14
      2/3
      0M 1M
      \c
      "" "abc"
      'sym
      :kw
      ()  ; '(1 2)
      [] [1 2]
      {}  ; {:a 1 :b 2}
      #{} ; #{1 2}
  )
  ; cannot be cast to java.lang.Comparable
  (is (thrown? ClassCastException (sorted-set '(1 2) '(1 2))))
  (is (thrown? ClassCastException (sorted-set {:a 1 :b 2} {:a 1 :b 2})))
  (is (thrown? ClassCastException (sorted-set #{1 2} #{1 2})))

  (are [x y] (= x y)
      ; generating
      (sorted-set) #{}
      (sorted-set 1) #{1}
      (sorted-set 1 2) #{1 2}

      ; sorting
      (seq (sorted-set 5 4 3 2 1)) '(1 2 3 4 5)

      ; special cases
      (sorted-set nil) #{nil}
      (sorted-set 1 nil) #{nil 1}
      (sorted-set nil 2) #{nil 2}
      (sorted-set #{}) #{#{}} ))


(deftest test-sorted-set-by
  ; only compatible types can be used
  ; NB: not a ClassCastException, but a RuntimeException is thrown,
  ; requires discussion on whether this should be symmetric with test-sorted-set
  (is (thrown? Exception (sorted-set-by < 1 "a")))
  (is (thrown? Exception (sorted-set-by < '(1 2) [3 4])))

  ; creates set?
  (are [x] (set? x)
       (sorted-set-by <)
       (sorted-set-by < 1 2) )

  ; equal and unique
  (are [x] (and (= (sorted-set-by compare x) #{x})
                (= (sorted-set-by compare x x) (sorted-set-by compare x)))
      nil
      false true
      0 42
      0.0 3.14
      2/3
      0M 1M
      \c
      "" "abc"
      'sym
      :kw
      ()  ; '(1 2)
      [] [1 2]
      {}  ; {:a 1 :b 2}
      #{} ; #{1 2}
  )
  ; cannot be cast to java.lang.Comparable
  ; NB: not a ClassCastException, but a RuntimeException is thrown,
  ; requires discussion on whether this should be symmetric with test-sorted-set
  (is (thrown? Exception (sorted-set-by compare '(1 2) '(1 2))))
  (is (thrown? Exception (sorted-set-by compare {:a 1 :b 2} {:a 1 :b 2})))
  (is (thrown? Exception (sorted-set-by compare #{1 2} #{1 2})))

  (are [x y] (= x y)
      ; generating
      (sorted-set-by >) #{}
      (sorted-set-by > 1) #{1}
      (sorted-set-by > 1 2) #{1 2}

      ; sorting
      (seq (sorted-set-by < 5 4 3 2 1)) '(1 2 3 4 5)

      ; special cases
      (sorted-set-by compare nil) #{nil}
      (sorted-set-by compare 1 nil) #{nil 1}
      (sorted-set-by compare nil 2) #{nil 2}
      (sorted-set-by compare #{}) #{#{}} ))


(deftest test-set
  ; set?
  (are [x] (set? (set x))
      () '(1 2)
      [] [1 2]
      #{} #{1 2}
      {} {:a 1 :b 2}
      (into-array []) (into-array [1 2])
      "" "abc" )

  ; unique
  (are [x] (= (set [x x]) #{x})
      nil
      false true
      0 42
      0.0 3.14
      2/3
      0M 1M
      \c
      "" "abc"
      'sym
      :kw
      () '(1 2)
      [] [1 2]
      {} {:a 1 :b 2}
      #{} #{1 2} )

  ; conversion
  (are [x y] (= (set x) y)
      () #{}
      '(1 2) #{1 2}

      [] #{}
      [1 2] #{1 2}

      #{} #{}         ; identity
      #{1 2} #{1 2}   ; identity

      {} #{}
      {:a 1 :b 2} #{[:a 1] [:b 2]}

      (into-array []) #{}
      (into-array [1 2]) #{1 2}

      "" #{}
      "abc" #{\a \b \c} ))


(deftest test-disj
  ; doesn't work on lists, vectors or maps
  (is (thrown? ClassCastException (disj '(1 2) 1)))
  (is (thrown? ClassCastException (disj [1 2] 1)))
  (is (thrown? ClassCastException (disj {:a 1} :a)))

  ; identity
  (are [x] (= (disj x) x)
      nil
      #{}
      #{1 2 3}
      ; different data types
      #{nil
        false true
        0 42
        0.0 3.14
        2/3
        0M 1M
        \c
        "" "abc"
        'sym
        :kw
        [] [1 2]
        {} {:a 1 :b 2}
        #{} #{1 2}} )

  ; type identity
  (are [x] (= (class (disj x)) (class x))
      (hash-set)
      (hash-set 1 2)
      (sorted-set)
      (sorted-set 1 2) )

  (are [x y] (= x y)
      (disj nil :a) nil
      (disj nil :a :b) nil

      (disj #{} :a) #{}
      (disj #{} :a :b) #{}

      (disj #{:a} :a) #{}
      (disj #{:a} :a :b) #{}
      (disj #{:a} :c) #{:a}

      (disj #{:a :b :c :d} :a) #{:b :c :d}
      (disj #{:a :b :c :d} :a :d) #{:b :c}
      (disj #{:a :b :c :d} :a :b :c) #{:d}
      (disj #{:a :b :c :d} :d :a :c :b) #{}

      (disj #{nil} :a) #{nil}
      (disj #{nil} #{}) #{nil}
      (disj #{nil} nil) #{}

      (disj #{#{}} nil) #{#{}}
      (disj #{#{}} #{}) #{}
      (disj #{#{nil}} #{nil}) #{} ))


;; *** Queues ***

(deftest test-queues
  (let [EMPTY clojure.lang.PersistentQueue/EMPTY]
    (are [x y] (= x y)
      EMPTY EMPTY
      (into EMPTY (range 50)) (into EMPTY (range 50))
      (conj EMPTY (Long. -1)) (conj EMPTY (Integer. -1))
      (hash (conj EMPTY (Long. -1))) (hash (conj EMPTY (Integer. -1)))
      (hash [1 2 3]) (hash (conj EMPTY 1 2 3))
      (range 5) (into EMPTY (range 5))
      (range 1 6) (-> EMPTY
                    (into (range 6))
                    pop))
    (are [x y] (not= x y)
      (range 5) (into EMPTY (range 6))
      (range 6) (into EMPTY (range 5))
      (range 0 6) (-> EMPTY
                    (into (range 6))
                    pop)
      (range 1 6) (-> EMPTY
                    (into (range 7))
                    pop))))


(deftest test-duplicates
  (let [equal-sets-incl-meta (fn [s1 s2]
                               (and (= s1 s2)
                                    (let [ss1 (sort s1)
                                          ss2 (sort s2)]
                                      (every? identity
                                              (map #(and (= %1 %2)
                                                         (= (meta %1) (meta %2)))
                                                   ss1 ss2)))))
        all-equal-sets-incl-meta (fn [& ss]
                                   (every? (fn [[s1 s2]]
                                             (equal-sets-incl-meta s1 s2))
                                           (partition 2 1 ss)))
        equal-maps-incl-meta (fn [m1 m2]
                               (and (= m1 m2)
                                    (equal-sets-incl-meta (set (keys m1))
                                                          (set (keys m2)))
                                    (every? #(= (meta (m1 %)) (meta (m2 %)))
                                            (keys m1))))
        all-equal-maps-incl-meta (fn [& ms]
                                   (every? (fn [[m1 m2]]
                                             (equal-maps-incl-meta m1 m2))
                                           (partition 2 1 ms)))
        cmp-first #(> (first %1) (first %2))
        x1 (with-meta [1] {:me "x"})
        y2 (with-meta [2] {:me "y"})
        z3a (with-meta [3] {:me "z3a"})
        z3b (with-meta [3] {:me "z3b"})
        v4a (with-meta [4] {:me "v4a"})
        v4b (with-meta [4] {:me "v4b"})
        v4c (with-meta [4] {:me "v4c"})
        w5a (with-meta [5] {:me "w5a"})
        w5b (with-meta [5] {:me "w5b"})
        w5c (with-meta [5] {:me "w5c"})]

    ;; Sets
    (is (thrown? IllegalArgumentException
                 (read-string "#{1 2 3 4 1 5}")))
    ;; If there are duplicate items when doing (conj #{} x1 x2 ...),
    ;; the behavior is that the metadata of the first item is kept.
    (are [s x] (all-equal-sets-incl-meta s
                                         (apply conj #{} x)
                                         (set x)
                                         (apply hash-set x)
                                         (apply sorted-set x)
                                         (apply sorted-set-by cmp-first x))
      #{x1 y2} [x1 y2]
      #{x1 z3a} [x1 z3a z3b]
      #{w5b}    [w5b w5a w5c]
      #{z3a x1} [z3a z3b x1])

    ;; Maps
    (is (thrown? IllegalArgumentException
                 (read-string "{:a 1, :b 2, :a -1, :c 3}")))
    ;; If there are duplicate keys when doing (assoc {} k1 v1 k2 v2
    ;; ...), the behavior is that the metadata of the first duplicate
    ;; key is kept, but mapped to the last value with an equal key
    ;; (where metadata of keys are not compared).
    (are [h x] (all-equal-maps-incl-meta h
                                         (apply assoc {} x)
                                         (apply hash-map x)
                                         (apply sorted-map x)
                                         (apply sorted-map-by cmp-first x)
                                         (apply array-map x))
      {x1 2, z3a 4} [x1 2, z3a 4]
      {x1 2, z3a 5} [x1 2, z3a 4, z3b 5]
      {z3a 5}       [z3a 2, z3a 4, z3b 5]
      {z3b 4, x1 5} [z3b 2, z3a 4, x1 5]
      {z3b v4b, x1 5} [z3b v4a, z3a v4b, x1 5]
      {x1 v4a, w5a v4c, v4a z3b, y2 2} [x1 v4a, w5a v4a, w5b v4b,
                                        v4a z3a, y2 2, v4b z3b, w5c v4c])))

(deftest test-array-map-arity
  (is (thrown? IllegalArgumentException
               (array-map 1 2 3))))

(deftest test-assoc
  (are [x y] (= x y)
       [4] (assoc [] 0 4)
       [5 -7] (assoc [] 0 5 1 -7)
       {:a 1} (assoc {} :a 1)
       {nil 1} (assoc {} nil 1)
       {:a 2 :b -2} (assoc {} :b -2 :a 2))
  (is (thrown? IllegalArgumentException (assoc [] 0 5 1)))
  (is (thrown? IllegalArgumentException (assoc {} :b -2 :a))))

(defn is-same-collection [a b]
  (let [msg (format "(class a)=%s (class b)=%s a=%s b=%s"
                    (.getName (class a)) (.getName (class b)) a b)]
    (is (= (count a) (count b) (.size a) (.size b)) msg)
    (is (= a b) msg)
    (is (= b a) msg)
    (is (.equals ^Object a b) msg)
    (is (.equals ^Object b a) msg)
    (is (= (hash a) (hash b)) msg)
    (is (= (.hashCode ^Object a) (.hashCode ^Object b)) msg)))

(deftest ordered-collection-equality-test
  (let [empty-colls [ []
                      '()
                      (lazy-seq)
                      clojure.lang.PersistentQueue/EMPTY
                      (vector-of :long) ]]
    (doseq [c1 empty-colls, c2 empty-colls]
      (is-same-collection c1 c2)))
  (let [colls1 [ [-3 :a "7th"]
                 '(-3 :a "7th")
                 (lazy-seq (cons -3
                   (lazy-seq (cons :a
                     (lazy-seq (cons "7th" nil))))))
                 (into clojure.lang.PersistentQueue/EMPTY
                       [-3 :a "7th"])
                 (sequence (map identity) [-3 :a "7th"]) ]]
    (doseq [c1 colls1, c2 colls1]
      (is-same-collection c1 c2)))
  (is-same-collection [-3 1 7] (vector-of :long -3 1 7)))

(defn case-indendent-string-cmp [s1 s2]
  (compare (string/lower-case s1) (string/lower-case s2)))

(deftest set-equality-test
  (let [empty-sets [ #{}
                     (hash-set)
                     (sorted-set)
                     (sorted-set-by case-indendent-string-cmp) ]]
    (doseq [s1 empty-sets, s2 empty-sets]
      (is-same-collection s1 s2)))
  (let [sets1 [ #{"Banana" "apple" "7th"}
                (hash-set "Banana" "apple" "7th")
                (sorted-set "Banana" "apple" "7th")
                (sorted-set-by case-indendent-string-cmp "Banana" "apple" "7th") ]]
    (doseq [s1 sets1, s2 sets1]
      (is-same-collection s1 s2))))

(deftest map-equality-test
  (let [empty-maps [ {}
                     (hash-map)
                     (array-map)
                     (sorted-map)
                     (sorted-map-by case-indendent-string-cmp) ]]
    (doseq [m1 empty-maps, m2 empty-maps]
      (is-same-collection m1 m2)))
  (let [maps1 [ {"Banana" "like", "apple" "love", "7th" "indifferent"}
                (hash-map "Banana" "like", "apple" "love", "7th" "indifferent")
                (array-map "Banana" "like", "apple" "love", "7th" "indifferent")
                (sorted-map "Banana" "like", "apple" "love", "7th" "indifferent")
                (sorted-map-by case-indendent-string-cmp
                               "Banana" "like", "apple" "love", "7th" "indifferent") ]]
    (doseq [m1 maps1, m2 maps1]
      (is-same-collection m1 m2))))

;; *** Collection hashes ***
;; See: http://clojure.org/data_structures#hash

(defn hash-ordered [collection]
  (-> (reduce (fn [acc e] (unchecked-add-int (unchecked-multiply-int 31 acc) (hash e)))
              1
              collection)
      (mix-collection-hash (count collection))))

(defn hash-unordered [collection]
  (-> (reduce unchecked-add-int 0 (map hash collection))
      (mix-collection-hash (count collection))))

(defn gen-elements
  []
  (gen/vec gen/anything))

(defspec ordered-collection-hashes-match
  identity
  [^{:tag clojure.test-clojure.data-structures/gen-elements} elem]
  (let [v (vec elem)
        l (apply list elem)]
    (is (= (hash v)
           (hash l)
           (hash (map identity elem))
           (hash-ordered elem)))))

(defspec unordered-set-hashes-match
  identity
  [^{:tag clojure.test-clojure.data-structures/gen-elements} elem]
  (let [unique-elem (distinct elem)
        s (into #{} unique-elem)]
    (is (= (hash s)
           (hash-unordered unique-elem)))))

(deftest ireduce-reduced
  (let [f (fn [_ a] (if (= a 5) (reduced "foo")))]
    (is (= "foo" (.reduce ^clojure.lang.IReduce (list 1 2 3 4 5) f)))
    (is (= "foo" (.reduce ^clojure.lang.IReduce (seq (long-array [1 2 3 4 5])) f)))))

(defn seq-iter-match
  [^clojure.lang.Seqable seqable ^Iterable iterable]
  (if (nil? iterable)
    (when (not (nil? (seq seqable)))
      (throw (ex-info "Null iterable but seq has elements"
                      {:pos 0 :seqable seqable :iterable iterable})))
    (let [i (.iterator iterable)]
      (loop [s (seq seqable)
             n 0]
        (if (seq s)
          (do
            (when-not (.hasNext i)
              (throw (ex-info "Iterator exhausted before seq"
                              {:pos n :seqable seqable :iterable iterable})))
              (when-not (= (.next i) (first s))
                (throw (ex-info "Iterator and seq did not match"
                                {:pos n :seqable seqable :iterable iterable})))
                (recur (rest s) (inc n)))
          (when (.hasNext i)
            (throw (ex-info "Seq exhausted before iterator"
                            {:pos n :seqable seqable :iterable iterable}))))))))

(deftest test-seq-iter-match
  (let [maps (mapcat #(vector (apply array-map %)
                              (apply hash-map %)
                              (apply sorted-map %))
                     [[] [nil 1] [nil 1 2 3] [1 2 3 4]])]
    (doseq [m maps]
      (seq-iter-match m m)
      (seq-iter-match (keys m) (keys m))
      (seq-iter-match (vals m) (vals m))
      (seq-iter-match (rest (keys m)) (rest (keys m)))
      (seq-iter-match (rest (vals m)) (rest (vals m))))))

(defn gen-map
  []
  (gen/hash-map (rand-nth cgen/ednable-scalars) (rand-nth cgen/ednable-scalars)))

(defspec seq-and-iter-match-for-maps
  identity
  [^{:tag clojure.test-clojure.data-structures/gen-map} m]
  (seq-iter-match m m))

(defn gen-set
  []
  (gen/set (rand-nth cgen/ednable-scalars)))

(defspec seq-and-iter-match-for-sets
  identity
  [^{:tag clojure.test-clojure.data-structures/gen-set} s]
  (seq-iter-match s s))

(defn gen-queue
  []
  (into clojure.lang.PersistentQueue/EMPTY
        (gen/vec (rand-nth cgen/ednable-scalars))))

(defspec seq-and-iter-match-for-queues
  identity
  [^{:tag clojure.test-clojure.data-structures/gen-queue} q]
  (seq-iter-match q q))

(defrecord Rec [a b])

(defn gen-record
  []
  (let [r (->Rec (gen/int) (gen/int))]
       (gen/one-of r
                   (merge r (gen-map)))))

(defspec seq-and-iter-match-for-records
         identity
         [^{:tag clojure.test-clojure.data-structures/gen-record} r]
         (seq-iter-match r r))

(defspec seq-and-iter-match-for-keys
         identity
         [^{:tag clojure.test-clojure.data-structures/gen-map} m]
         (seq-iter-match (keys m) (keys m)))

(defspec seq-and-iter-match-for-vals
         identity
         [^{:tag clojure.test-clojure.data-structures/gen-map} m]
         (seq-iter-match (vals m) (vals m)))

(defstruct test-struct :a :b)

(defn gen-struct
  []
  (let [s (struct test-struct (gen/int) (gen/int))]
    (gen/one-of s
                (assoc s (rand-nth cgen/ednable-scalars) (rand-nth cgen/ednable-scalars)))))

(defspec seq-and-iter-match-for-structs
         identity
         [^{:tag clojure.test-clojure.data-structures/gen-struct} s]
         (seq-iter-match s s))

Other Java examples (source code examples)

Here is a short list of links related to this Java data_structures.clj source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.