Easy lists with Apache Groovy
Groovy’s Lists go beyond Java’s basics, offering rich syntax and methods for effortless data manipulation. Flexible types, intuitive operators and powerful methods put data control in your hands.
This is way beyond the base functionality provided by the java.util.List interface and its various implementations, which are already pretty cool. (If you haven’t installed Groovy yet, please read the intro to this series.)
A good place to start looking at List in Groovy is some basic syntactical elements. Here are a few:
1 def testList = []
2 testList << 1
3 testList << "Hi there"
4 testList << java.time.LocalDate.of(2023,03,01)
5 testList[15] = 2
6 testList[-2] = 1.5
7 testList << [3,4,5]
8 println "testList $testList"
9 println "testList.class ${testList.class}"
10 println "2 in testList ${2 in testList}"
Line 1 defines the variable testList and sets it to the empty list.
Lines 2-4 append instances of an int (Integer), a String, and a java.time.LocalDate, demonstrating that Groovy is agnostic about lists of a certain type.
Line 5 sticks the int (Integer) value 2 in position 15 (the sixteenth element of the list). This has the side effect of inserting nulls in the list from position 3 through 14.
Line 6 uses a negative index, which Groovy interprets as counting from the right, to insert the decimal (BigDecimal) number 1.5 in the penultimate list element: -1 is the right-most element. I think of these negative indices as the number to be subtracted from the length of the list to get to the list element in question.
Line 7 appends the list [3, 4, 5] to the list, making it a sub-list.
Line 8 prints the list out and Line 9 tells you what sort of beast you’ve created.
Line 10 uses the in operator to determine whether any element has the value 2.
Running this script yields:
$ groovy Groovy12a.groovy
testList [1, Hi there, 2023-03-01, null, null, null, null, null, null, null, null, null, null, null, 1.5, 2, [3, 4, 5]]
testList.class class java.util.ArrayList
2 in testList true
You should note that the nulls mentioned above and in the sub-list are in the last element. Also, as you know, the second-last element of the list is 2 and therefore the in operator yields true.
Groovy adds many methods to the List interface. One important thing to keep in mind is that some of these methods mutate the list and others create a new list from the old, applying the modifications.
For example, plus() (which can be accessed by the + operator) creates a new list that is the concatenation of the two lists using the method (or the plus operator. Using minus() produces a new list by removing all instances of the second item or list. Whereas removeAt() modifies the list by removing the element at the position given by its argument:
1 def l1 = 'Lists are where Groovy gets way more interesting than the base functionality provided by the java.util.List interface and its various implementations, which are already pretty cool.'.split() as List
2 def l2 = 'A good place to start looking at List in Groovy is some basic syntactical elements.'.split() as List
3 def l3 = l1 + l2
4 def l4 = l1 - 'the'
5 def l5 = l2.clone()
6 l5.removeAt(9)
7 println "l1 $l1"
8 println "l2 $l2"
9 println "l3 $l3"
10 println "l4 $l4"
11 println "l5 $l5"
Lines 1-2 create two lists l1 and l2 by applying the split() function to the second and third sentences of this article, creating two arrays, and then the as operator is applied to convert those arrays to List instances.
Line 3 applies the + operator (which is the plus() method) to create a new list, l3, which is the concatenation of the two lists l1 and l2.
Line 4 applies the - operator (which is the minus() method) to create a new list, l4, which is list l1 with all elements equal to “the” removed.
Line 5-6 creates l5 as a copy of l2 with the clone() method and removes the 9th element from l5. Note that if you just assigned l2 to l5, then both variables would point to the same list.
Lines 7-11 print the lists.
When you run this, you see:
$ groovy Groovy12b.groovy
l1 [Lists, are, where, Groovy, gets, way, more, interesting, than, the, base, functionality, provided, by, the, java.util.List, interface, and, its, various, implementations,, which, are, already, pretty, cool.]
l2 [A, good, place, to, start, looking, at, List, in, Groovy, is, some, basic, syntactical, elements.]
l3 [Lists, are, where, Groovy, gets, way, more, interesting, than, the, base, functionality, provided, by, the, java.util.List, interface, and, its, various, implementations,, which, are, already, pretty, cool., A, good, place, to, start, looking, at, List, in, Groovy, is, some, basic, syntactical, elements.]
l4 [Lists, are, where, Groovy, gets, way, more, interesting, than, base, functionality, provided, by, java.util.List, interface, and, its, various, implementations,, which, are, already, pretty, cool.]
l5 [A, good, place, to, start, looking, at, List, in, is, some, basic, syntactical, elements.]
Another syntactic support comes from using a range within brackets to reference a sublist or slice of the List instance:
1 def l1 = [0,1,2,3,4,5,6,7,8,9]
2 def l2 = l1[3..5]
3 println "l1 $l1"
4 println "l2 $l2"
When you run this, you see:
$ groovy Groovy12c.groovy
l1 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
l2 [3, 4, 5]
Many of the really cool methods added to the List interface use a Closure parameter to do interesting things to the list. You’ll see some of these things in an upcoming article on closures.
For LISP aficionados, Groovy List provides head() and tail() methods which implement LISP operations car and cdr respectively.
This is a good point to suggest a diversion to the Groovy List interface reference description and also a read of the Groovy documentation on List.
Conclusion
Groovy elevates the pedestrian Java List implementations to the near sublime. Groovy does this by adding many useful methods and providing operators that use some of those common methods in a more readable and compact fashion.
One main difference from Java is that Groovy doesn’t operate from the point of view that List instances should contain elements of the same type. Groovy doesn’t enforce the element type when type checking is not enabled. For instance:
1 def l = new ArrayList<Integer>()
2 l << 1
3 l << 2
4 l << "3"
5 println l
It compiles and executes just fine:
$ groovy Groovy12e.groovy
[1, 2, 3]
Stay tuned for the next installment in the series, where we’ll look into getting closure.
