Below are some ways on how we can declare or create a Set data structure.
We can declare the type to be Set just like in Java. Example:
class Test { static main(args) { Set stringSet = ["apple", "apple", "banana", "banana"] println "size = ${stringSet.size()}" stringSet.each { println it } } }
Even when there are duplicates in the given collection, only unique elements will be stored. Hence, the output will be:
size = 2 banana apple
We can use the toSet() function to convert a List to a Set.
class Test { static main(args) { def myCollection = ["apple", "apple", "banana", "banana"] println "collection size = ${myCollection.size()}" def mySet = myCollection.toSet() println "set size = ${mySet.size()}" } }
The expected output is:
collection size = 4 set size = 2
We can also use "as Set" to express that we are declaring a Set.
class Test { static main(args) { def myCollection = ["apple", "apple", "banana", "banana"] def myFirstSet = myCollection as Set def mySecondSet = ["apple", "apple", "banana", "banana"] as Set println "first set size = ${myFirstSet.size()}" println "second set size = ${mySecondSet.size()}" } }
Since it is a set, only unique values are retained. Hence the output will be:
first set size = 2 second set size = 2
Below are the specific class implementation of Set created, depending on how we declared the Set.
class Test { static main(args) { Set myFirstSet = ["apple", "apple", "banana", "banana"] def mySecondSet = ["apple", "apple", "banana", "banana"].toSet() def myThirdSet = ["apple", "apple", "banana", "banana"] as Set println "first set class = ${myFirstSet.getClass()}" println "second set class = ${mySecondSet.getClass()}" println "third set class = ${myThirdSet.getClass()}" } }
Declaring a variable as type Set or invoking toSet() will make it use HashSet. While using "as Set" uses LinkedHashSet.
first set class = class java.util.HashSet second set class = class java.util.HashSet third set class = class java.util.LinkedHashSet
We can use "as" to declare a specific implementation of Set if we want to control the behavior. For example:
class Test { static main(args) { Set myFirstSet = ["apple", "apple", "banana", "banana"] as LinkedHashSet def mySecondSet = ["apple", "apple", "banana", "banana"] as TreeSet def myThirdSet = ["apple", "apple", "banana", "banana"] as HashSet println "first set class = ${myFirstSet.getClass()}" println "second set class = ${mySecondSet.getClass()}" println "third set class = ${myThirdSet.getClass()}" } }
And the instances are what we declared it to be as shown by the output:
first set class = class java.util.LinkedHashSet second set class = class java.util.TreeSet third set class = class java.util.HashSet
Below are some operations supported by Set in Groovy
asImmutable() - we can use asImmutable() if we want to force the Set to be an immutable object. This is important for thread safety because it guarantees state of objects can never be changed. Below is an example:class Test { static main(args) { def myCollection = ["apple", "banana", "carrots"].toSet().asImmutable() println "class: ${myCollection.getClass()}" myCollection.add("Grapes") } }
And sice it is immutable, adding items to it will cause an exception:
class: class java.util.Collections$UnmodifiableSet Caught: java.lang.UnsupportedOperationException java.lang.UnsupportedOperationException at java_util_Set$add.call(Unknown Source) at test.Test.main(Test.groovy:9)
asSynchronized() - again another method useful for thread safety concerns:
class Test { static main(args) { def myCollection = ["apple", "banana", "carrots"].toSet().asSynchronized() println "class: ${myCollection.getClass()}" } }
Internally, it uses SynchronizedSet as the implementation.
class: class java.util.Collections$SynchronizedSet
each - we can use each to iterate over the items of a Set. For example:
class Test { static main(args) { Set mySet = ["apple", "banana", "carrots"] mySet.each { item -> println item } } }
And the output are the items printed on each line:
banana apple carrots
eachWithIndex - this can be used to also iterate over the items in a Set but also provides an index for each item.
class Test { static main(args) { Set mySet = ["apple", "banana", "carrots"] mySet.eachWithIndex{ item, idx -> println "Index # ${idx} is: ${item}" } } }
Below is the expected output where the index starts from 0.
Index # 0 is: banana Index # 1 is: apple Index # 2 is: carrots
findAll - this can be used to filter the items on a list based on some criteria. Below is an example:
class Test { static main(args) { Set mySet = ["apple", "banana", "carrots"] println mySet.findAll {it.contains("s")} } }
And the output is the subset of the list that has an "s" in it's letters:
[carrots]
flatten() - flatten can be used to flatten a list when some of it's elements are also a collection. Here is an example:
class Test { static main(args) { Set mySet = [["apple", "banana"], "carrots", ["egg", "guava"]] println mySet println mySet.flatten() } }
And the output is the flattened items in the list of lists.
[[apple, banana], carrots, [egg, guava]] [apple, banana, carrots, egg, guava]
grep - this can be used to find items in a Set. When used without parameters, it looks for all elements in a Set that are not 0, null, empty String, or empty collection. Example:
class Test { static main(args) { Set mySet = [10, 20, 0, false, true, '', 'test', [], null, ""] println mySet.grep() } }
And the result are the collection of items with no null, 0 or empty String/Collection.
[20, test, 10, true]
We can also use grep to filter by data type. For example:
class Test { static main(args) { Set mySet = [10, 20, 0, false, true, 'hello', [1, 2], [3, 4], null, "world"] println mySet.grep(Number) println mySet.grep(String) println mySet.grep(Boolean) println mySet.grep(Collection) } }
The result is shown below where items for each data type are shown:
[0, 20, 10] [world, hello] [false, true] [[1, 2], [3, 4]]
We can also use grep to match by regular expression. Below is an example of using grep to find item that starts with letter b.
class Test { static main(args) { Set mySet = ["apple", "banana", "carrots"] println mySet.grep(~/b.+/) } }
And the output is:
[banana]
We can also use grep by using any logical expression just like in findall. For example:
class Test { static main(args) { Set mySet = ["apple", "banana", "carrots"] println mySet.grep{it.contains("e")} } }
The result is:
[apple]
intersect - this method can be used to find the intersection between two collections. Here is an example:
class Test { static main(args) { Set firstSet = [1, 2, 3, 4, 5] Set secondSet = [4, 5, 6, 7, 8] println firstSet.intersect(secondSet) } }
Since the common items between the two sets are 4 and 5, the output of the code will be:
[4, 5]
leftShift we can use the left shift operator to add items to a Set. Here are two ways on how to use this:
class Test { static main(args) { Set mySet = [1, 2, 3, 4, 5] mySet.leftShift(6) mySet << 7 println mySet } }
Since we added 6 and 7, the output will be:
[1, 2, 3, 4, 5, 6, 7]
minus - we can use minus to subtract elements from a Set that exists on another Collection. Below are two ways of invoking minus:
class Test { static main(args) { Set mySet = [1, 2, 3, 4, 5] println mySet.minus([4, 5]) println mySet - [1, 2] } }
And the output expected is:
[1, 2, 3] [3, 4, 5]
plus - we can use plus to combine another collection to our Set. Here are two ways of doing that:
class Test { static main(args) { Set mySet = [1, 2, 3, 4, 5] println mySet.plus([6, 7]) println mySet + [9, 10] } }
And the output is:
[1, 2, 3, 4, 5, 6, 7] [1, 2, 3, 4, 5, 9, 10]
split - we can use split to divide a set into two. One set contains those that satisfies the given criteria, while the other contains all that failed. Below is an example:
class Test { static main(args) { Set mySet = [1000, 2000, 3000, 4000, 5000, 6000] println mySet.split{it> 3000} } }
Since the criteria is items greater than 3000, all items that are bigger than 3000 are grouped together, while those that failed are grouped in another collection:
[[4000, 6000, 5000], [2000, 1000, 3000]]