Should You Use ForEach or InjectInto in Eclipse Collections?
Learn the forEach
and injectInto
patterns in Eclipse Collections.
To forEach or to injectInto?
In Eclipse Collections, forEach
and injectInto
are both internal iterators that provide the most basic of the iteration patterns. A developer can use both of these patterns to accomplish a large number of iteration tasks. As internal iterators, these methods encapsulate the implementation details of “how to” iterate over the elements of the collection, and leave it to the developer to use a lambda to specify the behavior that should be applied to each element of a collection.
The forEach
and injectInto
patterns can be used as the building blocks for many of the other iteration patterns (e.g. select
, reject
, collect
, etc.). Both patterns iterate over a collection from the beginning of the collection to the end and perform some operation that receives each element of the collection as a parameter. Neither the forEach
nor the injectInto
pattern can be used effectively to implement any of the short-circuiting patterns (e.g. detect
, anySatisfy
, allSatisfy
, noneSatisfy
).
The functionality of forEach
and injectInto
can be summarized as follows.
forEach
takes a single argumentProcedure
and returnsvoid
. Each elements of the collection is passed to theProcedure
.injectInto
takes a two argumentFunction2
. A value is injected in as the first parameter of theFunction2
along with each element of the collection. The method returns the final result that theFunction2
returns after the last element is processed.
There are specializations of both patterns. The injectInto
pattern has more primitive specializations than forEach
because injectInto
returns some type and it may return one of the eight primitive types, or Object
. The forEach
patterns always return void
.
One of the things that injectInto
can do, that forEach
cannot, is perform a side effect free operation. With forEach
, a side effect always happens somewhere. That is its purpose — to make side effects happen with elements of the collection. There may be a mutation applied to an element of the collection, or a mutation to a variable in the scope of a lambda, or the element is printed to System.out
, etc.
With injectInto
, it is possible to perform a side effect free operation because it injects, returns, and re-injects some result from the two-argument Function
. The value that is injected and returned may be immutable, like a String
or Integer
or a primitive value.
I’m going to show some examples of using forEach
and injectInto
to solve similar problems. This should begin to make it clear when one of the patterns is more suitable or desirable than the other. The answer of whether one is more desirable than another will be an exercise left up to you, the reader. In some cases, forEach
will be more readable. In many cases, injectInto
will be more flexible, safe and/or performant.
The following are the examples I will show:
- Count
- Sum
- Convert to Set
Example: Count
In the following examples, I will count the even integers contained in a MutableList<Integer>
and MutableIntList
. There is a method named count
defined on both Object and Primitive collections in Eclipse Collections that takes a Predicate
as a parameter. The intent of these examples is to show some options for implementing count
using forEach
or injectInto
patterns.
1. Count: forEach (Object List)
I had to create a mutable Counter
in order to record the total count of even values. I used the ternary operator to write a simplified if expression to determine whether to add a value of 1
or 0
to the Counter
.
2. Count: forEach (Primitive List)
The only difference between the forEach
on the object and primitive lists is that the int
values in the primitive List
don’t require unboxing as the Integer
values do in the object List
. This approach still requires having an effectively final mutable Counter
object to increment in the lambda.
3. Count: injectInto (Object List)
This implementation is side-effect free. We start with an initial Integer
value of 0
, and add a 1
or 0
to that result for each element if it is even. There is a lot of unboxing and auto-boxing here as the result of each function will unbox values and then auto-box the result.
4. Count: injectInto (Primitive List)
This example uses a MutableIntList
which does not box the int
values in the List
. The result of injectInto
is still an object, so the results are auto-boxed for each element.
5. Count: injectIntoInt (Object List)
The specialization injectIntoInt
allows an int
value of 0
to be injected into the iteration and returned as the result. There will be no auto-boxing of the results here, but the values are unboxed in order to test if they are even. This solution is both side-effect free and results in zero auto-boxing. The values in the integers List
however are still boxed as it is a MutableList<Integer>
.
6. Count: injectIntoInt (Primitive List)
The specialization injectIntoInt
on a MutableIntList
allows for the implementation of side-effect free counting with no boxing of int
values to Integer
objects in either the list or the calculations.
Example: Sum
In the following examples, I will sum the integers contained in a MutableList<Integer>
and MutableIntList
. There is a method named sumOfInt
defined on Object collections and simply sum
on Primitive collections in Eclipse Collections. The intent of these examples is to show some options for implementing sum
using forEach
or injectInto
patterns.
1. Sum: forEach (Object List)
I used LongAdder
from the JDK to accumulate the sum. There is an add
method on LongAdder
that takes a long
. Each Integer
object in the integers List is unboxed and cast to a long
.
2. Sum: forEach (PrimitiveList)
The primitive version of sum with forEach
is essentially the same as the object version, just without requiring the unboxing of Integer
objects in the call to add
on LongAdder
.
3. Sum: injectInto (Object List)
By injecting an initial value of Long.valueOf(0)
, the return type of injectInto
will be a Long
. I used the Long::sum
method reference which takes two long
values as parameters. The result is unboxed from Long
to long
and the Integer
values in the List
are unboxed and cast to long
.
4. Sum: injectInto (Primitive List)
The primitive version of sum using injectInto
using Long
as an injected value is essentially the same as the object version. The one difference is there is no unboxing of Integer
objects required.
5. Sum: injectIntoLong (Object List)
Using injectIntoLong
on an object List and injecting an initial long
value of 0L
, the return type of injectIntoLong
is a primitive long
. I used the Long::sum
method reference which takes two long
values as parameters. The result is passed in as a long
and the Integer
values in the List are unboxed and cast to long
.
6. Sum: injectIntoLong (Primitive List)
Using injectIntoLong
on a primitive List and injecting an initial long
value of 0L
, the return type of injectIntoLong
is a primitive long
. I used the Long::sum method reference which takes two long
values as parameters. The result is passed in as a long and the int
values in the primitive List are cast to long
.
Example: Convert to Set
In the following examples, I will add the integers contained in a MutableList<Integer>
or MutableIntList
into a MutableSet<Integer>
or MutableIntSet
. I will also show how injectInto
can be used to add to an ImmutableSet<Integer>
or ImmutableIntSet
.
There are converter methods on Object and Primitive collections for converting from one type of container to another (e.g. toSet
, toImmutableSet
). The intent of these examples is to show some options for implementing toSet
and toImmutableSet
using the forEach
or injectInto
patterns.
1. To Mutable Set: forEach (Object List)
I create a MutableSet<Integer>
named set
to hold the elements I want to transfer from integers
. Then using forEach, I pass the method reference set::add
. This code is really simple and straightforward.
2. To Mutable Int Set: forEach (Primitive List)
The primitive version is essentially same as the object version, with the target set
being MutableIntSet
.
3. To MutableSet: injectInto (Object List)
Using injectInto
, I inject the MutableSet<Integer>
as the first parameter of the Function2
. I use the MutableSet::with
method reference which matches the two-parameter types required by the Function2
.
The first parameter type isMutableSet<Integer>
and the second parameter type is the type of each element of the collection (Integer
).
The return type of with
on any MutableCollection
in Eclipse Collections is the collection type itself, since with
calls add
and then returns this
.
4. To MutableIntSet: injectInto (Primitive List)
The primitive version is essentially same as the object version, with the target set
being MutableIntSet
.
5. To ImmutableSet: injectInto (Object List)
Converting a MutableList
to an ImmutableSet
id another example that demonstrates the flexibility of injectInto
over forEach
. An ImmutableSet
has no mutating methods. There is no add
method like a MutableSet
. There is a newWith
method which creates a new ImmutableSet
by copying the original set and adding an element to create a new ImmutableSet
. I pass an empty ImmutableSet
into injectInto
along with a method reference to ImmutableSet::newWith
.
6. To Immutable Int Set: injectInto (Primitive List)
The primitive version is essentially same as the object version, with the target set
being MutableIntSet
.
Additional examples of forEach
Sometimes you might need an index with your forEach
. Thankfully, in Eclipse Collections, there is forEachWithIndex
for this purpose. Here’s a blog that describes methods that provide indexes including forEachWithIndex
.
There is also a blog that describes a specialized form of forEach
named forEachInBoth
. Since this blog was originally written, I have added forEachInBoth
directly to Object and primitive List
containers in Eclipse Collections.
Additional examples of injectInto
There are a couple of blogs that cover more examples of injectInto
as well as specialized forms of injectInto
like injectIntoKeyValue
. The following blog covers some general examples of injectInto
.
The following blog describes and shows examples of injectIntoKeyValue
which works with Maps and primitive Maps in Eclipse Collections. The blog was written by Emilie Robichaud.
Some final thoughts on forEach and injectInto
I find it is very easy to start thinking about a problem using forEach
. If you need to cause some basic side effect, forEach
is a very simple go to method.
Sometimes after I have written something using forEach
, I wonder what the same code would look like if I used injectInto
. I will use injectInto
if I want to have a side effect free operation, or to control any side effects fully within the context of the Function2
that I pass to injectInto
.
So should you use forEach
or injectInto
in Eclipse Collections? As with many things, the best solution may be subjective, and I would recommend going with the code that you find the easiest to read and understand.
If you do learn how to add injectInto
to your bag of tricks, you may start to be amazed at its power and flexibility, even if its mystery never quite goes away.
Source for Examples
Want to Connect?I am a Project Lead and Committer for the Eclipse Collections OSS project at the Eclipse Foundation. Eclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.