Better Programming

Advice for programmers.

Should You Use ForEach or InjectInto in Eclipse Collections?

Donald Raab
Better Programming
Published in
10 min readJun 2, 2022

--

Many of the forEach / 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 argument Procedure and returns void. Each elements of the collection is passed to the Procedure.
  • injectInto takes a two argument Function2. A value is injected in as the first parameter of the Function2 along with each element of the collection. The method returns the final result that the Function2 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)

Using forEach to count even numbers in an 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)

Using forEach to count even numbers in a 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)

Using injectInto to count even numbers in an 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)

Using injectInto to count even numbers in a 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)

Using injectIntoInt to count even numbers in an 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)

Using injectIntoInt to count evens in a 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)

Using forEach to sum the numbers in an 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)

Using forEach to sum the numbers in a Primitive List

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)

Using injectInto to sum the numbers in an 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)

Using injectInto to sum the numbers in a 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 to sum the numbers in an 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 to sum the numbers in a 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)

Using forEach to add elements of a List to a MutableSet

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)

Using forEach to add elements of a MutableIntList to a MutableIntSet

The primitive version is essentially same as the object version, with the target set being MutableIntSet.

3. To MutableSet: injectInto (Object List)

Using injectInto to add elements of a MutableList to a MutableSet

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)

Using injectInto to add elements of a MutableIntList to a MutableIntSet

The primitive version is essentially same as the object version, with the target set being MutableIntSet.

5. To ImmutableSet: injectInto (Object List)

Using injectInto to add elements of a MutableList to an ImmutableSet

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)

Using injectInto to add elements of a MutableIntList to an ImmutableIntSet

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

Source gist for examples in this blog
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.

--

--

Donald Raab
Donald Raab

Written by Donald Raab

Java Champion. Creator of the Eclipse Collections OSS Java library (https://github.com/eclipse-collections). Inspired by Smalltalk. Opinions are my own.